Postgres: Bagaimana SET TIDAK NULL "lebih efisien" daripada PERIKSA kendala


17

Dalam dokumen PostgreSQL untuk Kendala , katanya

Batasan bukan-nol secara fungsional setara dengan membuat batasan periksa CHECK (column_name IS NOT NULL), tetapi dalam PostgreSQL membuat batasan bukan-nol eksplisit lebih efisien.

aku bertanya-tanya

  • Apa sebenarnya yang dimaksud dengan "lebih efisien"?
  • Apa kerugian menggunakan CHECK (column_name IS NOT NULL)bukan SET NOT NULL?

Saya ingin dapat menambahkan NOT VALID CHECKkendala dan memvalidasinya secara terpisah (jadi AccessExclusiveLockhanya ditahan untuk waktu yang singkat untuk penambahan kendala dan kemudian ShareUpdateExclusiveLockditahan untuk langkah validasi yang lebih lama):

ALTER TABLE table_name
  ADD CONSTRAINT column_constraint
  CHECK (column_name IS NOT NULL)
  NOT VALID;
ALTER TABLE table_name
  VALIDATE CONSTRAINT column_constraint;

Dari pada:

ALTER TABLE table_name
  ALTER COLUMN column_name
  SET NOT NULL;


Seperti apa rencana eksekusi jika Anda melakukan not indengan kedua varian? Apakah mereka sama atau berbeda?
Martin Smith

Jawaban:


12

Tebakan liar saya: "lebih efisien" berarti "lebih sedikit waktu diperlukan untuk melakukan pemeriksaan" (keunggulan waktu). Ini juga bisa berarti "lebih sedikit memori yang diperlukan untuk melakukan pemeriksaan" (keunggulan ruang). Mungkin juga berarti "memiliki lebih sedikit efek samping" (seperti tidak mengunci sesuatu atau menguncinya untuk periode waktu yang lebih singkat) ... tetapi saya tidak memiliki cara untuk mengetahui atau memeriksa "keuntungan ekstra".

Saya tidak bisa memikirkan cara mudah untuk memeriksa kemungkinan keuntungan ruang (yang, saya kira, tidak begitu penting ketika memori saat ini murah). Di sisi lain, itu tidak sulit untuk memeriksa kemungkinan waktu keuntungan: hanya membuat dua tabel yang sama, dengan satu-satunya pengecualian dari kendala tersebut. Masukkan jumlah baris yang cukup besar, ulangi beberapa kali, dan periksa waktunya.

Ini adalah pengaturan tabel:

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);

Ini adalah tabel tambahan, yang digunakan untuk menyimpan timing:

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)
) ;

Dan ini adalah tes yang dilakukan, menggunakan pgAdmin III, dan fitur 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;

Hasilnya dirangkum dalam kueri berikut:

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 ;

Dengan hasil sebagai berikut:

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

Grafik nilai menunjukkan variabilitas penting:

Waktu yang dihabiskan untuk setiap 200000 baris yang dimasukkan (dalam detik)

Jadi, dalam praktiknya, PERIKSA (kolom BUKAN NULL) sangat sedikit lebih lambat (0,5%). Namun, perbedaan kecil ini bisa disebabkan oleh alasan acak, asalkan variabilitas waktunya jauh lebih besar dari itu. Jadi, ini tidak signifikan secara statistik.

Dari sudut pandang praktis, saya akan sangat mengabaikan "lebih efisien" NOT NULL, karena saya tidak benar-benar melihatnya penting; sedangkan saya berpikir bahwa tidak adanya suatu AccessExclusiveLockadalah keuntungan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.