Mi conjetura: "más eficiente" significa "se requiere menos tiempo para realizar la verificación" (ventaja de tiempo). También puede significar "se requiere menos memoria para realizar la verificación" (ventaja de espacio). También podría significar "tiene menos efectos secundarios" (como no bloquear algo o bloquearlo por períodos más cortos de tiempo) ... pero no tengo forma de saber o verificar esa "ventaja adicional".
No puedo pensar en una manera fácil de verificar una posible ventaja de espacio (que, supongo, no es tan importante cuando la memoria hoy en día es barata). Por otro lado, no es tan difícil verificar la posible ventaja de tiempo: solo cree dos tablas que sean iguales, con la única excepción de la restricción. Inserte un número suficientemente grande de filas, repita varias veces y verifique los tiempos.
Esta es la configuración de la tabla:
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 es una tabla adicional, utilizada para almacenar tiempos:
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)
) ;
Y esta es la prueba realizada, usando pgAdmin III, y la función 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;
El resultado se resume en la siguiente 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 ;
Con los siguientes 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
Un gráfico de los valores muestra una variabilidad importante:
Entonces, en la práctica, el CHEQUE (columna NO ES NULO) es un poco más lento (en un 0.5%). Sin embargo, esta pequeña diferencia puede deberse a cualquier razón aleatoria, siempre que la variabilidad de los tiempos sea mucho mayor que eso. Entonces, no es estadísticamente significativo.
Desde un punto de vista práctico, ignoraría mucho el "más eficiente" NOT NULL
, porque realmente no veo que sea significativo; Considerando que creo que la ausencia de un AccessExclusiveLock
es una ventaja.