Meu palpite: "mais eficiente" significa "menos tempo para executar a verificação" (vantagem de tempo). Também pode significar "menos memória é necessária para executar a verificação" (vantagem de espaço). Também pode significar "tem menos efeitos colaterais" (como não travar algo ou travá-lo por períodos mais curtos) ... mas não tenho como saber ou verificar essa "vantagem extra".
Não consigo pensar em uma maneira fácil de verificar uma possível vantagem de espaço (o que, eu acho, não é tão importante quando a memória hoje em dia é barata). Por outro lado, não é tão difícil verificar a possível vantagem de tempo: basta criar duas tabelas iguais, com a única exceção da restrição. Insira um número suficientemente grande de linhas, repita algumas vezes e verifique os tempos.
Esta é a configuração da tabela:
CREATE TABLE t1
(
id serial PRIMARY KEY,
value integer NOT NULL
) ;
CREATE TABLE t2
(
id serial PRIMARY KEY,
value integer
) ;
ALTER TABLE t2
ADD CONSTRAINT explicit_check_not_null
CHECK (value IS NOT NULL);
Esta é uma tabela extra, usada para armazenar horários:
CREATE TABLE timings
(
test_number integer,
table_tested integer /* 1 or 2 */,
start_time timestamp without time zone,
end_time timestamp without time zone,
PRIMARY KEY(test_number, table_tested)
) ;
E este é o teste realizado, usando o pgAdmin III e o recurso pgScript .
declare @trial_number;
set @trial_number = 0;
BEGIN TRANSACTION;
while @trial_number <= 100
begin
-- TEST FOR TABLE t1
-- Insert start time
INSERT INTO timings(test_number, table_tested, start_time)
VALUES (@trial_number, 1, clock_timestamp());
-- Do the trial
INSERT INTO t1(value)
SELECT 1.0
FROM generate_series(1, 200000) ;
-- Insert end time
UPDATE timings
SET end_time=clock_timestamp()
WHERE test_number=@trial_number and table_tested = 1;
-- TEST FOR TABLE t2
-- Insert start time
INSERT INTO timings(test_number, table_tested, start_time)
VALUES (@trial_number, 2, clock_timestamp());
-- Do the trial
INSERT INTO t2(value)
SELECT 1.0
FROM generate_series(1, 200000) ;
-- Insert end time
UPDATE timings
SET end_time=clock_timestamp()
WHERE test_number=@trial_number and table_tested = 2;
-- Increase loop counter
set @trial_number = @trial_number + 1;
end
COMMIT TRANSACTION;
O resultado é resumido na seguinte consulta:
SELECT
table_tested,
sum(delta_time),
avg(delta_time),
min(delta_time),
max(delta_time),
stddev_pop(delta_time)
FROM
(
SELECT
table_tested, extract(epoch from (end_time - start_time)) AS delta_time
FROM
timings
) AS delta_times
GROUP BY
table_tested
ORDER BY
table_tested ;
Com os seguintes resultados:
table_tested | sum | min | max | avg | stddev_pop
-------------+---------+-------+-------+-------+-----------
1 | 176.740 | 1.592 | 2.280 | 1.767 | 0.08913
2 | 177.548 | 1.593 | 2.289 | 1.775 | 0.09159
Um gráfico dos valores mostra uma variabilidade importante:
Portanto, na prática, o CHECK (a coluna NÃO É NULL) é um pouco mais lento (0,5%). No entanto, essa pequena diferença pode ser devido a qualquer motivo aleatório, desde que a variabilidade dos tempos seja muito maior que isso. Portanto, não é estatisticamente significativo.
Do ponto de vista prático, eu ignoraria muito o "mais eficiente" NOT NULL
, porque realmente não vejo isso significativo; considerando que a ausência de um AccessExclusiveLock
é uma vantagem.