39.7. Правила против триггеров#

39.7. Правила против триггеров

39.7. Правила против триггеров

Многие вещи, которые можно сделать с помощью триггеров, также могут быть реализованы с помощью системы правил Tantor SE. Однако некоторые виды ограничений, особенно внешние ключи, нельзя реализовать с помощью правил. Возможно разместить квалифицированное правило, которое переписывает команду на 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

В любом из этих случаев, дополнительные команды из системы правил будут более или менее независимы от количества затронутых строк в команде.

Сводка заключается в том, что правила будут значительно медленнее триггеров только в том случае, если их действия приводят к большим и плохо квалифицированным соединениям, ситуация, в которой планировщик терпит неудачу.