CREATE TRIGGER — создание нового триггера
CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGERname{ BEFORE | AFTER | INSTEAD OF } {event[ OR ... ] } ONtable_name[ FROMreferenced_table_name] [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ] [ REFERENCING { { OLD | NEW } TABLE [ AS ]transition_relation_name} [ ... ] ] [ FOR [ EACH ] { ROW | STATEMENT } ] [ WHEN (condition) ] EXECUTE { FUNCTION | PROCEDURE }function_name(arguments) гдеeventможет быть одним из: INSERT UPDATE [ OFcolumn_name[, ... ] ] DELETE TRUNCATE
CREATE TRIGGER создаёт новый триггер.
CREATE OR REPLACE TRIGGER либо создаст новый триггер,
либо заменит существующий триггер.
Триггер будет связан с указанной таблицей, представлением или внешней таблицей
и будет выполнять указанную функцию function_name, когда
над этой таблицей выполняются определённые операции.
Чтобы заменить текущее определение существующего триггера, используйте
CREATE OR REPLACE TRIGGER, указав имя существующего триггера
и родительскую таблицу. Все остальные свойства заменяются.
Триггер можно указать для срабатывания до того, как
операция будет предпринята над строкой (до проверки ограничений и
попытки выполнения INSERT, UPDATE или
DELETE); или после завершения операции
(после проверки ограничений и завершения
INSERT, UPDATE или
DELETE); или вместо операции
(в случае вставок, обновлений или удалений в представлении).
Если триггер срабатывает до или вместо события, он может пропустить
операцию для текущей строки или изменить вставляемую строку (только для
операций INSERT и UPDATE).
Если триггер срабатывает после события, все изменения, включая
эффекты других триггеров, «видны»
для этого триггера.
Триггер, помеченный как FOR EACH ROW, вызывается
один раз для каждой строки, которую модифицирует операция. Например,
команда DELETE, затрагивающая 10 строк, вызовет любые
триггеры ON DELETE на целевом отношении
10 раз отдельно, один раз для каждой удалённой строки. В отличие от этого,
триггер, помеченный как FOR EACH STATEMENT,
выполняется только один раз для данной операции, независимо от того, сколько строк
она модифицирует (в частности, операция, которая не модифицирует ни одной строки,
всё равно приведёт к выполнению любых применимых триггеров FOR
EACH STATEMENT).
Триггеры, указанные для срабатывания INSTEAD OF события,
должны быть помечены как FOR EACH ROW и могут быть определены только
на представлениях. Триггеры BEFORE и AFTER на представлении
должны быть помечены как FOR EACH STATEMENT.
Кроме того, триггеры могут быть определены для срабатывания по команде
TRUNCATE, хотя только как
FOR EACH STATEMENT.
Следующая таблица суммирует, какие типы триггеров могут использоваться на таблицах, представлениях и внешних таблицах:
| Когда | Событие | Построчный | На уровне оператора |
|---|---|---|---|
BEFORE | INSERT/UPDATE/DELETE | Таблицы и внешние таблицы | Таблицы, представления и внешние таблицы |
TRUNCATE | — | Таблицы и внешние таблицы | |
AFTER | INSERT/UPDATE/DELETE | Таблицы и внешние таблицы | Таблицы, представления и внешние таблицы |
TRUNCATE | — | Таблицы и внешние таблицы | |
INSTEAD OF | INSERT/UPDATE/DELETE | Представления | — |
TRUNCATE | — | — |
Кроме того, в определении триггера можно указать логическое условие WHEN,
которое будет проверяться, чтобы определить, должен ли триггер сработать.
В построчных триггерах условие WHEN может
проверять старые и/или новые значения столбцов строки.
Триггеры на уровне оператора также могут иметь условия WHEN, хотя эта возможность
не так полезна для них, поскольку условие не может ссылаться на какие-либо значения
в таблице.
Если для одного события определено несколько триггеров одного вида, они будут срабатывать в алфавитном порядке по имени.
При указании опции CONSTRAINT эта команда создаёт
триггер ограничения (constraint trigger).
Это то же самое, что обычный триггер,
за исключением того, что время срабатывания триггера можно регулировать с помощью
SET CONSTRAINTS.
Триггеры ограничений должны быть триггерами AFTER ROW на обычных
таблицах (не внешних таблицах).
Они могут срабатывать либо в конце оператора, вызывающего событие,
либо в конце содержащей транзакции; в последнем случае они
называются отложенными (deferred). Ожидающее отложенное срабатывание триггера
также может быть принудительно выполнено немедленно с помощью SET
CONSTRAINTS. Ожидается, что триггеры ограничений вызывают исключение,
когда нарушаются реализуемые ими ограничения.
Опция REFERENCING позволяет собирать
переходные отношения (transition relations), то есть наборы строк, включающие все
строки, вставленные, удалённые или изменённые текущим оператором SQL.
Эта возможность позволяет триггеру видеть глобальное представление о том, что сделал оператор,
а не только по одной строке за раз. Эта опция разрешена только для
триггеров AFTER, которые не являются триггерами ограничений; также, если
триггер является триггером UPDATE, он не должен указывать
список column_name.
OLD TABLE может быть указан только один раз и только для триггера,
который может срабатывать по событиям UPDATE или DELETE; он создаёт
переходное отношение, содержащее предыдущие образы (before-images) всех строк,
обновлённых или удалённых оператором.
Аналогично, NEW TABLE может быть указан только один раз и только для
триггера, который может срабатывать по событиям UPDATE или INSERT;
он создаёт переходное отношение, содержащее последующие образы (after-images)
всех строк, обновлённых или вставленных оператором.
Команда SELECT не изменяет никакие строки, поэтому нельзя
создавать триггеры на SELECT. Правила и представления могут предоставлять
рабочие решения проблем, которые, кажется, требуют триггеров SELECT.
Дополнительную информацию о триггерах см. в Глава 5.2.
name
Имя нового триггера. Оно должно отличаться от
имени любого другого триггера для той же таблицы.
Имя не может быть указано с квалификацией схемы — триггер наследует
схему своей таблицы. Для триггера ограничения это также имя, которое
используется при изменении поведения триггера с помощью
SET CONSTRAINTS.
BEFOREAFTERINSTEAD OF
Определяет, вызывается ли функция до, после или вместо
события. Триггер ограничения может быть указан только как
AFTER.
event
Одно из значений: INSERT, UPDATE,
DELETE или TRUNCATE;
оно определяет событие, которое вызовет триггер. Несколько
событий можно указать с помощью OR, за исключением случаев, когда
запрашиваются переходные отношения.
Для событий UPDATE можно
указать список столбцов, используя следующий синтаксис:
UPDATE OFcolumn_name1[,column_name2... ]
Триггер сработает только если хотя бы один из перечисленных столбцов
упоминается как цель команды UPDATE
или если один из перечисленных столбцов является генерируемым столбцом, который зависит от
столбца, являющегося целью команды UPDATE.
События INSTEAD OF UPDATE не допускают список столбцов.
Список столбцов также не может быть указан при запросе переходных отношений.
table_nameИмя (возможно, с указанием схемы) таблицы, представления или внешней таблицы, для которой предназначен триггер.
referenced_table_nameИмя (возможно, с указанием схемы) другой таблицы, на которую ссылается ограничение. Этот параметр используется для ограничений внешнего ключа и не рекомендуется для общего использования. Он может быть указан только для триггеров ограничений.
DEFERRABLENOT DEFERRABLEINITIALLY IMMEDIATEINITIALLY DEFERREDВремя срабатывания триггера по умолчанию. Подробности об этих параметрах ограничений см. в документации CREATE TABLE. Это может быть указано только для триггеров ограничений.
REFERENCINGЭто ключевое слово непосредственно предшествует объявлению одного или двух имён отношений, которые предоставляют доступ к переходным отношениям оператора, вызвавшего срабатывание.
OLD TABLENEW TABLEЭто предложение указывает, является ли следующее имя отношения именем для переходного отношения предыдущих образов (before-image) или последующих образов (after-image).
transition_relation_nameИмя (без квалификатора), которое будет использоваться внутри триггера для этого переходного отношения.
FOR EACH ROWFOR EACH STATEMENT
Определяет, должна ли функция триггера срабатывать
один раз для каждой строки, затронутой событием триггера, или только один раз
за оператор SQL. Если ни одно не указано, по умолчанию используется FOR EACH
STATEMENT. Триггеры ограничений могут быть указаны только
как FOR EACH ROW.
condition
Логическое выражение, определяющее, будет ли функция триггера
фактически выполнена. Если указано WHEN,
функция будет вызвана только если condition возвращает true.
В триггерах FOR EACH ROW условие WHEN
может ссылаться на столбцы старых и/или новых значений строк,
записывая OLD. или
column_nameNEW. соответственно.
Конечно, триггеры column_nameINSERT не могут ссылаться на OLD,
а триггеры DELETE не могут ссылаться на NEW.
Триггеры INSTEAD OF не поддерживают условия WHEN.
В настоящее время выражения WHEN не могут содержать
подзапросы.
Обратите внимание, что для триггеров ограничений оценка условия WHEN
не откладывается, а происходит сразу после выполнения операции обновления строки.
Если условие не вычисляется в true, то триггер не ставится в очередь на отложенное выполнение.
function_name
Предоставленная пользователем функция, объявленная как не принимающая аргументов
и возвращающая тип trigger, которая выполняется при
срабатывании триггера.
В синтаксисе CREATE TRIGGER ключевые слова
FUNCTION и PROCEDURE
эквивалентны, но упоминаемая функция в любом случае должна быть функцией,
а не процедурой. Использование ключевого слова PROCEDURE
здесь историческое и устарело.
argumentsНеобязательный список аргументов, разделённых запятыми, который будет предоставлен функции при выполнении триггера. Аргументы являются строковыми константами. Простые имена и числовые константы также могут быть записаны здесь, но они все будут преобразованы в строки. Пожалуйста, проверьте описание языка реализации функции триггера, чтобы узнать, как эти аргументы могут быть доступны внутри функции; это может отличаться от обычных аргументов функции.
Для создания или замены триггера на таблице пользователь должен иметь привилегию
TRIGGER на этой таблице. Пользователь также должен
иметь привилегию EXECUTE на функцию триггера.
Используйте DROP TRIGGER для удаления триггера.
Создание построчного триггера на секционированной таблице вызовет создание идентичного
«клон»-триггера на каждой из её существующих
секций; и любые секции, созданные или присоединённые позже, также будут иметь
идентичный триггер. Если в дочерней секции уже существует триггер с конфликтующим именем,
возникает ошибка, если только не используется CREATE OR REPLACE
TRIGGER, и в этом случае этот триггер заменяется на
клон-триггер. Когда секция отделяется от родительской, её клон-триггеры
удаляются.
Триггер, специфичный для столбца (определённый с использованием синтаксиса UPDATE OF
), сработает, когда любой
из его столбцов указан в качестве цели в списке column_nameSET
команды UPDATE. Возможно изменение значения столбца
даже тогда, когда триггер не срабатывает, потому что изменения, внесённые в содержимое строки
триггерами BEFORE UPDATE, не учитываются.
И наоборот, команда вида UPDATE ... SET x = x ...
вызовет триггер на столбце x, даже если значение столбца
не изменилось.
В триггере BEFORE условие WHEN
вычисляется непосредственно перед тем, как функция будет или должна быть выполнена, поэтому использование
WHEN не сильно отличается от проверки того же самого
условия в начале функции триггера. Обратите внимание, в частности,
что строка NEW, видимая условию, является текущим значением,
возможно, изменённым предыдущими триггерами. Кроме того, условию WHEN
триггера BEFORE не разрешается проверять системные столбцы
строки NEW (такие как ctid),
потому что они ещё не будут установлены.
В триггере AFTER условие WHEN
вычисляется сразу после обновления строки и определяет, будет ли
событие поставлено в очередь для срабатывания триггера в конце оператора. Поэтому когда
условие WHEN триггера AFTER не возвращает
true, не нужно ставить событие в очередь и повторно извлекать строку в конце
оператора. Это может привести к значительному ускорению выполнения операторов, которые
изменяют много строк, если триггер нужно срабатывать только для нескольких из этих
строк.
В некоторых случаях возможно, чтобы одна команда SQL вызывала более чем
один вид триггеров. Например, команда INSERT с
предложением ON CONFLICT DO UPDATE может вызывать как операции вставки, так и
обновления, поэтому при необходимости она вызовет оба вида триггеров.
Переходные отношения, предоставляемые триггерам,
специфичны для их типа события; таким образом, триггер INSERT
увидит только вставленные строки, а триггер UPDATE
увидит только обновлённые строки.
Обновления или удаления строк, вызванные действиями по обеспечению внешнего ключа, такими как
ON UPDATE CASCADE или ON DELETE SET NULL,
рассматриваются как часть команды SQL, которая их вызвала (обратите внимание, что такие
действия никогда не откладываются). Соответствующие триггеры в затронутой таблице будут
срабатывать, что предоставляет ещё один способ, которым команда SQL может
вызывать триггеры, не соответствующие напрямую её типу. В простых случаях триггеры,
которые запрашивают переходные отношения, увидят все изменения, вызванные в их
таблице одной исходной командой SQL, как единое переходное отношение.
Однако бывают случаи, когда наличие триггера AFTER ROW,
который запрашивает переходные отношения, приведёт к тому, что действия по обеспечению внешнего ключа,
вызванные одной командой SQL, будут разделены на
несколько шагов, каждый со своими собственными переходными отношениями (или наборами отношений).
В таких случаях любые присутствующие триггеры на уровне оператора будут срабатывать один раз за
создание набора переходных отношений, гарантируя, что триггеры видят
каждую затронутую строку в переходном отношении один и только один раз.
Триггеры на уровне оператора на представлении срабатывают только если действие над представлением
обрабатывается построчным триггером INSTEAD OF.
Если действие обрабатывается правилом INSTEAD, то
любые операторы, порождённые правилом, выполняются вместо
исходного оператора, называющего представление, так что триггеры, которые будут
срабатывать, — это триггеры на таблицах, указанных в заменяющих операторах.
Аналогично, если представление автоматически обновляемо, то действие
обрабатывается автоматическим перезаписыванием оператора в действие над
базовой таблицей представления, так что срабатывают триггеры на уровне оператора
базовой таблицы.
Модификация секционированной таблицы или таблицы с дочерними таблицами наследования вызывает
срабатывание триггеров на уровне оператора, прикреплённых к явно указанной таблице, но не
триггеров на уровне оператора для её секций или дочерних таблиц.
Напротив, построчные триггеры срабатывают на строках в затронутых секциях или
дочерних таблицах, даже если они не указаны явно в запросе.
Если триггер на уровне оператора был определён с переходными отношениями,
указанными в предложении REFERENCING, то предыдущие и последующие
образы строк видны из всех затронутых секций или дочерних таблиц.
В случае дочерних таблиц наследования образы строк включают только столбцы,
которые присутствуют в таблице, к которой прикреплён триггер.
В настоящее время построчные триггеры с переходными отношениями не могут быть определены
на секциях или дочерних таблицах наследования. Также триггеры на секционированных
таблицах не могут быть триггерами INSTEAD OF.
В настоящее время опция OR REPLACE не поддерживается для
триггеров ограничений.
Не рекомендуется заменять существующий триггер в транзакции, которая уже выполнила обновляющие действия над таблицей триггера. Решения о срабатывании триггера или части этих решений, которые уже были приняты, не будут пересмотрены, поэтому последствия могут быть неожиданными.
Существует несколько встроенных функций-триггеров, которые могут быть использованы для решения распространённых проблем без необходимости писать собственный код триггера; см. Раздел 2.6.29.
Выполнять функцию check_account_update всякий раз, когда
строка таблицы accounts собирается быть обновлена:
CREATE TRIGGER check_update
BEFORE UPDATE ON accounts
FOR EACH ROW
EXECUTE FUNCTION check_account_update();
Изменить это определение триггера, чтобы функция выполнялась только если
столбец balance указан в качестве цели в
команде UPDATE:
CREATE OR REPLACE TRIGGER check_update
BEFORE UPDATE OF balance ON accounts
FOR EACH ROW
EXECUTE FUNCTION check_account_update();
Эта форма выполняет функцию только если значение столбца balance
фактически изменилось:
CREATE TRIGGER check_update
BEFORE UPDATE ON accounts
FOR EACH ROW
WHEN (OLD.balance IS DISTINCT FROM NEW.balance)
EXECUTE FUNCTION check_account_update();
Вызывать функцию для регистрации обновлений таблицы accounts, но только если
что-то изменилось:
CREATE TRIGGER log_update
AFTER UPDATE ON accounts
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE FUNCTION log_account_update();
Выполнять функцию view_insert_row для каждой строки, чтобы вставлять
строки в таблицы, лежащие в основе представления:
CREATE TRIGGER view_insert
INSTEAD OF INSERT ON my_view
FOR EACH ROW
EXECUTE FUNCTION view_insert_row();
Выполнять функцию check_transfer_balances_to_zero для каждого
оператора, чтобы подтвердить, что строки в таблице transfer компенсируются до нулевой суммы:
CREATE TRIGGER transfer_insert
AFTER INSERT ON transfer
REFERENCING NEW TABLE AS inserted
FOR EACH STATEMENT
EXECUTE FUNCTION check_transfer_balances_to_zero();
Выполнять функцию check_matching_pairs для каждой строки, чтобы
подтвердить, что изменения вносятся в совпадающие пары одновременно (тем же
оператором):
CREATE TRIGGER paired_items_update
AFTER UPDATE ON paired_items
REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab
FOR EACH ROW
EXECUTE FUNCTION check_matching_pairs();
Раздел 5.2.4 содержит полный пример функции-триггера, написанной на C.
Оператор CREATE TRIGGER в
Digital Q.DataBase реализует подмножество стандарта
SQL. В настоящее время отсутствуют следующие функциональные возможности:
Хотя имена переходных таблиц для триггеров AFTER
указываются с помощью предложения REFERENCING стандартным способом,
переменные строк, используемые в триггерах FOR EACH ROW, не могут быть указаны в
предложении REFERENCING. Они доступны способом,
который зависит от языка, на котором написана функция триггера,
но фиксирован для любого конкретного языка. Некоторые языки
эффективно ведут себя так, как если бы существовало предложение REFERENCING,
содержащее OLD ROW AS OLD NEW ROW AS NEW.
Стандарт позволяет использовать переходные таблицы с
триггерами UPDATE, специфичными для столбцов, но тогда набор строк,
которые должны быть видны в переходных таблицах, зависит от
списка столбцов триггера. Это в настоящее время не реализовано в
Digital Q.DataBase.
Digital Q.DataBase позволяет выполнять только
пользовательскую функцию в качестве действия триггера. Стандарт
позволяет выполнять ряд других команд SQL, таких как
CREATE TABLE, в качестве действия триггера. Это
ограничение несложно обойти, создав пользовательскую функцию,
которая выполняет нужные команды.
Стандарт SQL указывает, что несколько триггеров должны срабатывать в порядке их создания. В Digital Q.DataBase используется порядок по имени, который был признан более удобным.
Стандарт SQL указывает, что триггеры BEFORE DELETE на каскадных
удалениях срабатывают после завершения каскадного удаления DELETE.
В Digital Q.DataBase поведение таково, что BEFORE
DELETE всегда срабатывает до действия удаления, даже каскадного.
Это считается более последовательным. Также существует нестандартное
поведение, если триггеры BEFORE изменяют строки или предотвращают
обновления во время обновления, вызванного действием ссылочной целостности. Это может
привести к нарушениям ограничений или хранимым данным, которые не соблюдают
ссылочное ограничение.
Возможность указывать несколько действий для одного триггера с помощью
OR является расширением Digital Q.DataBase по отношению к
стандарту SQL.
Возможность срабатывания триггеров по команде TRUNCATE является
расширением Digital Q.DataBase по отношению к стандарту SQL, как и
возможность определять триггеры на уровне оператора на представлениях.
CREATE CONSTRAINT TRIGGER является
расширением Digital Q.DataBase по отношению к стандарту SQL.
Как и опция OR REPLACE.