Постгреси: як НАЗАД НЕ НУЛЬНИЙ "ефективніше", ніж обмеження ПЕРЕВІРИТИ


17

У документах PostgreSQL для обмежень це сказано

Ненульове обмеження функціонально еквівалентне створенню контрольного обмеження CHECK (column_name IS NOT NULL), але в PostgreSQL створення явного ненульового обмеження є більш ефективним.

я задаюся питанням

  • Що саме означає "ефективніший"?
  • Які шкоди використовувати CHECK (column_name IS NOT NULL)замість цього SET NOT NULL?

Я хочу мати змогу додати NOT VALID CHECKобмеження та перевірити його окремо (тому AccessExclusiveLockутримується лише короткий проміжок часу для додавання обмеження, а потім ShareUpdateExclusiveLockутримується для більш тривалого етапу перевірки):

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

Замість:

ALTER TABLE table_name
  ALTER COLUMN column_name
  SET NOT NULL;


Як виглядають плани виконання, якщо ви робите not inз обома варіантами? Вони однакові чи вони відрізняються?
Мартін Сміт

Відповіді:


12

Моя дика здогадка: "ефективніше" означає "для здійснення перевірки потрібно менше часу" (перевагу в часі). Це також може означати, що для перевірки потрібно менше пам'яті (перевагу місця). Це також може означати, що "має менше побічних ефектів" (наприклад, не зафіксувати щось або заблокувати його на коротший проміжок часу) ... але у мене немає способу дізнатися або перевірити цю "додаткову перевагу".

Я не можу придумати простий спосіб перевірити можливу перевагу в просторі (що, я думаю, не так важливо, коли пам'ять сьогодні дешева). З іншого боку, перевірити можливу перевагу в часі не так складно: просто створіть дві таблиці, однакові, за винятком обмеження. Вставте достатньо велику кількість рядків, повторіть кілька разів і перевірте таймінги.

Це налаштування таблиці:

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

Це додаткова таблиця, яка використовується для зберігання таймінгів:

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

І це тест, виконаний за допомогою pgAdmin III та функції 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;

Результат підсумований у наступному запиті:

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 ;

З такими результатами:

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

Графік значень показує важливу мінливість:

Час, витрачений на кожну вкладку 200000 рядків (у секундах)

Так, на практиці ПЕРЕВІРКА (стовпець НЕ НУЛЬНИЙ) відбувається дещо повільніше (на 0,5%). Однак ця невелика різниця може бути пов’язана з будь-якою випадковою причиною за умови, що мінливість таймінгів набагато більша за цю. Отже, це не є статистично значущим.

З практичної точки зору я б дуже ігнорував "ефективніші" NOT NULL, тому що я не вважаю, що це важливо; тоді як я думаю, що відсутність AccessExclusiveLockпереваги є перевагою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.