Правила и триггеры
Эта страница переведена при помощи нейросети GigaChat.
Многие вещи, которые можно сделать с помощью триггеров, также могут быть реализованы с использованием системы правил PostgreSQL. Одной из вещей, которую нельзя реализовать с помощью правил, являются некоторые виды ограничений, особенно внешние ключи. Возможно разместить правило с ограничивающим условием, которое переписывает команду на NOTHING
, если значение столбца не появляется в другой таблице. Но тогда данные просто выбрасываются и это не очень хорошая идея. Если требуется проверка допустимых значений, а в случае недопустимого значения должно генерироваться сообщение об ошибке, это должно быть сделано с помощью триггера.
В этом разделе выполнен акцент на использовании правил для обновления представлений. Все примеры правил обновления в разделе также могут быть реализованы с использованием INSTEAD OF
триггеров на представлениях. Написание таких триггеров часто проще, чем написание правил, особенно если для выполнения обновления требуется сложная логика.
Что касается вещей, которые могут быть реализованы обоими способами, то выбор лучшего зависит от использования базы данных. Триггер срабатывает один раз для каждого затронутого ряда. Правило изменяет запрос или генерирует дополнительный запрос. Поэтому, если в одном операторе затрагивается много строк, правило, выдающее одну дополнительную команду, скорее всего, будет быстрее, чем триггер, который вызывается для каждой строки и должен много раз заново определять, что делать. Однако подход с триггером концептуально намного проще, чем подход с правилом, и новичкам легче разобраться с ним.
Ниже приведен пример того, как выбор между правилами и триггерами реализуется в одной ситуации. Есть две таблицы:
CREATE TABLE computer (
hostname text, -- indexed
manufacturer text -- indexed
);
CREATE TABLE software (
software text, -- indexed
hostname text -- indexed
);
В обеих таблицах тысячи строк, а индексы в hostname
уникальны. Правило или триггер должны реализовать ограничение, которое удаляет строки из software
, которые ссылаются на удаленный компьютер. Команда триггера будет следующей:
DELETE FROM software WHERE hostname = $1;
Поскольку триггер вызывается для каждой отдельной строки, удаленной из computer
, он может подготовить и сохранить план для этой команды и передать значение hostname
в параметре. Правило было бы написано следующим образом:
CREATE RULE computer_del AS ON DELETE TO computer
DO DELETE FROM software WHERE hostname = OLD.hostname;
Теперь рассмотрим различные типы удалений. В случае:
DELETE FROM computer WHERE hostname = 'mypc.local.net';
таблица computer
сканируется по индексу (быстро), и команда, выданная триггером, также будет использовать сканирование индекса (также быстро). Дополнительная команда из правила была бы:
DELETE FROM software WHERE computer.hostname = 'mypc.local.net'
AND software.hostname = computer.hostname;
Поскольку установлены соответствующие индексы, планировщик создаст план
Nestloop
-> Index Scan using comp_hostidx on computer
-> Index Scan using soft_hostidx on software
Так что не было бы такой большой разницы в скорости между реализацией триггера и правилом.
Со следующим удалением избавляемся от всех компьютеров 2000 года выпуска, где hostname
начинается с old
. Есть две возможные команды для этого. Одна из них:
DELETE FROM computer WHERE hostname >= 'old'
AND hostname < 'ole'
Команда, добавленная правилом, будет:
DELETE FROM software WHERE computer.hostname >= 'old' AND computer.hostname < 'ole'
AND software.hostname = computer.hostname;
С планом:
Hash Join
-> Seq Scan on software
-> Hash
-> Index Scan using comp_hostidx on computer
Другая возможная команда:
DELETE FROM computer WHERE hostname ~ '^old';
Что приводит к следующему плану выполнения команды, добавленной правилом:
Nestloop
-> Index Scan using comp_hostidx on computer
-> Index Scan using soft_hostidx on software
Это показывает, что планировщик не осознает, что квалификация для hostname
в computer
также может быть использована для сканирования индекса на software
при наличии нескольких выражений квалификации, объединенных с помощью AND
, что и делает версия команды с регулярным выражением. Триггер будет вызван один раз для каждого из 2000 старых компьютеров, которые должны быть удалены, и это приведет к одному сканированию индекса по computer
и 2000 сканирований индекса по software
. Реализация правила сделает это с двумя командами, использующими индексы. И зависит от общего размера таблицы software
, будет ли правило все еще быстрее в ситуации последовательного сканирования. 2000 выполнений команд триггера через менеджер SPI занимают некоторое время, даже если все блоки индекса вскоре окажутся в кеше.
Последняя команда, которую рассмотрим, выглядит так:
DELETE FROM computer WHERE manufacturer = 'bim';
И снова это могло бы привести к удалению множества строк из computer
. Поэтому триггер снова запустит множество команд через исполнитель. Команда, созданная правилом, будет выглядеть следующим образом:
DELETE FROM software WHERE computer.manufacturer = 'bim'
AND software.hostname = computer.hostname;
План для этой команды снова будет вложенным циклом по двум индексным сканированиям, только с использованием другого индекса на computer
:
Nestloop
-> Index Scan using comp_manufidx on computer
-> Index Scan using soft_hostidx on software
В любом из этих случаев дополнительные команды от системы правил будут более или менее независимыми от количества затронутых строк в команде.
Резюме заключается в том, что правила будут значительно медленнее триггеров только в том случае, если их действия приводят к большим и плохо квалифицированным соединениям, ситуации, когда планировщик терпит неудачу.