La mia ipotesi selvaggia: "più efficiente" significa "è necessario meno tempo per eseguire il controllo" (vantaggio di tempo). Può anche significare "è necessaria meno memoria per eseguire il controllo" (vantaggio di spazio). Potrebbe anche significare "ha meno effetti collaterali" (come non bloccare qualcosa o bloccarlo per periodi di tempo più brevi) ... ma non ho modo di sapere o verificare quel "vantaggio in più".
Non riesco a pensare a un modo semplice per verificare un possibile vantaggio di spazio (che, immagino, non è così importante quando la memoria al giorno d'oggi è economica). D'altra parte, non è così difficile verificare l'eventuale vantaggio temporale: basta creare due tabelle uguali, con la sola eccezione del vincolo. Inserisci un numero sufficientemente elevato di righe, ripeti alcune volte e controlla i tempi.
Questa è l'impostazione della tabella:
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);
Questa è una tabella aggiuntiva, utilizzata per memorizzare i tempi:
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 questo è il test eseguito, usando pgAdmin III, e la funzione 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;
Il risultato è riassunto nella seguente query:
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 i seguenti risultati:
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 grafico dei valori mostra un'importante variabilità:
Quindi, in pratica, il CONTROLLO (colonna NON È NULL) è leggermente più lento (dello 0,5%). Tuttavia, questa piccola differenza può essere dovuta a qualsiasi motivo casuale, a condizione che la variabilità dei tempi sia molto più grande di quella. Quindi, non è statisticamente significativo.
Da un punto di vista pratico, ignorerei molto il "più efficiente" NOT NULL
, perché non vedo davvero che sia significativo; mentre penso che l'assenza di un AccessExclusiveLock
sia un vantaggio.