Многие задачи, реализуемые с помощью триггеров, могут быть также решены через Digital Q.DataBase
систему правил. К числу задач, которые невозможно реализовать с помощью правил, относятся некоторые виды ограничений, в частности внешние ключи. Можно создать условное правило, которое переписывает команду в NOTHING
если значение столбца отсутствует в другой таблице. Однако в этом случае данные будут просто отброшены, что не является оптимальным решением. Если требуется проверка значений на корректность и при обнаружении недопустимого значения должно быть сформировано сообщение об ошибке, эту операцию должен выполнять триггер.
В данной главе основное внимание было уделено использованию правил для обновления представлений. Все примеры правил обновления в этой главе также могут быть реализованы с использованием INSTEAD OF триггеров для представлений. Написание таких
триггеров зачастую проще, чем написание правил, особенно если для выполнения обновления
требуется сложная логика.
В тех случаях, когда реализация возможна обоими способами, выбор оптимального зависит от особенностей использования базы данных. Триггер срабатывает один раз для каждой затронутой строки. Правило же модифицирует запрос или генерирует дополнительный запрос. Таким образом, если в одном операторе изменяется много строк, правило, выдающее одну дополнительную команду, вероятно, будет работать быстрее, чем триггер, который вызывается для каждой отдельной строки и должен многократно определять порядок действий. Тем не менее, подход с использованием триггеров концептуально гораздо проще, чем использование системы правил, и начинающим пользователям легче реализовать его корректно.
Ниже приведен пример, демонстрирующий различия между применением правил и триггеров в конкретной ситуации. Имеются две таблицы:
CREATE TABLE computer (
hostname text, -- индексировано
manufacturer text -- индексировано
);
CREATE TABLE software (
software text, -- индексировано
hostname text -- индексировано
);
Обе таблицы содержат многие тысячи строк, а индексы по полю
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 -> сканирование индекса с использованием comp_hostidx по таблице computer -> сканирование индекса с использованием soft_hostidx по таблице 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
-> сканирование индекса с использованием comp_hostidx для computer
Другая возможная команда:
DELETE FROM computer WHERE hostname ~ '^old';
что приводит к следующему плану выполнения для команды, добавленной правилом:
Nestloop -> сканирование индекса с использованием comp_hostidx по таблице computer -> сканирование индекса с использованием soft_hostidx по таблице 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 -> сканирование индекса с использованием comp_manufidx по таблице computer -> сканирование индекса с использованием soft_hostidx по таблице software
В любом из этих случаев выполнение дополнительных команд системы правил будет в той или иной степени независимо от количества строк, затронутых основной командой.
В итоге можно сказать, что правила будут работать существенно медленнее триггеров только в том случае, если их действия приводят к формированию больших соединений с неэффективными условиями выборки, то есть в ситуациях, когда планировщик не справляется.