{"Entry":{"collection":"sql","key":"create-trigger","name":"CREATE TRIGGER","aliases":["createtrigger"],"metadata":{"aliases":["createtrigger"],"changed_in":["6.5","7.0","7.4","9.0","9.1","9.2","9.5","10","11","14","19"],"changes":[{"from":"6.4","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":[],"removed":[]},"status":"changed","synopsis":{"added":["EXECUTE PROCEDURE ER\"\u003efuncBLE\u003e ( arguments )"],"removed":["EXECUTE PROCEDURE funcname ( arguments )"]},"to":"6.5"},{"from":"6.5","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","compatibility"],"removed":[]},"status":"changed","synopsis":{"added":["EXECUTE PROCEDURE func ( arguments )"],"removed":["EXECUTE PROCEDURE ER\"\u003efuncBLE\u003e ( arguments )"]},"to":"7.0"},{"from":"7.0","purpose_changed":false,"renamed":{"from_file":"sql-createtrigger.htm","to_file":"sql-createtrigger.html"},"sections":{"added":[],"changed":["description"],"removed":[]},"status":"changed","synopsis":null,"to":"7.1"},{"from":"7.1","purpose_changed":true,"renamed":null,"sections":{"added":["notes","examples","see_also"],"changed":["description","compatibility"],"removed":["usage"]},"status":"changed","synopsis":null,"to":"7.2"},{"from":"7.2","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","notes","compatibility","see_also"],"removed":[]},"status":"changed","synopsis":null,"to":"7.3"},{"from":"7.3","purpose_changed":false,"renamed":null,"sections":{"added":["parameters"],"changed":["description","notes","examples","compatibility","see_also"],"removed":[]},"status":"changed","synopsis":{"added":["ON table [ FOR [ EACH ] { ROW | STATEMENT } ]","EXECUTE PROCEDURE funcname ( arguments )"],"removed":["EXECUTE PROCEDURE func ( arguments )"]},"to":"7.4"},{"from":"7.4","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","examples","compatibility"],"removed":[]},"status":"changed","synopsis":null,"to":"8.0"},{"from":"8.0","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","examples","compatibility"],"removed":[]},"status":"changed","synopsis":null,"to":"8.1"},{"from":"8.1","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","examples","compatibility"],"removed":[]},"status":"changed","synopsis":null,"to":"8.2"},{"from":"8.2","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","parameters","notes","examples"],"removed":[]},"status":"changed","synopsis":null,"to":"8.3"},{"from":"8.3","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","parameters","notes","compatibility"],"removed":[]},"status":"changed","synopsis":null,"to":"8.4"},{"from":"8.4","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","parameters","notes","examples","compatibility"],"removed":[]},"status":"changed","synopsis":{"added":["[ WHEN ( condition ) ]","EXECUTE PROCEDURE function_name ( arguments )"],"removed":["EXECUTE PROCEDURE funcname ( arguments )"]},"to":"9.0"},{"from":"9.0","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","parameters","notes","examples","compatibility","see_also"],"removed":[]},"status":"changed","synopsis":{"added":["CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }","[ FROM referenced_table_name ]","[ NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } ]","INSERT","UPDATE [ OF column_name [, ... ] ]","DELETE","TRUNCATE"],"removed":[]},"to":"9.1"},{"from":"9.1","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["parameters"],"removed":[]},"status":"changed","synopsis":{"added":["ON table_name"],"removed":["ON table"]},"to":"9.2"},{"from":"9.2","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["notes","compatibility","see_also"],"removed":[]},"status":"changed","synopsis":null,"to":"9.3"},{"from":"9.3","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","parameters"],"removed":[]},"status":"changed","synopsis":null,"to":"9.4"},{"from":"9.4","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","notes"],"removed":[]},"status":"changed","synopsis":{"added":["[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]"],"removed":["[ NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } ]"]},"to":"9.5"},{"from":"9.5","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","examples"],"removed":[]},"status":"changed","synopsis":null,"to":"9.6"},{"from":"9.6","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","parameters","notes","examples","compatibility"],"removed":[]},"status":"changed","synopsis":{"added":["[ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]","[ FOR [ EACH ] { ROW | STATEMENT } ]"],"removed":[]},"to":"10"},{"from":"10","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","parameters","notes","examples"],"removed":[]},"status":"changed","synopsis":{"added":["EXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )"],"removed":[]},"to":"11"},{"from":"11","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","parameters","notes","examples"],"removed":[]},"status":"changed","synopsis":null,"to":"12"},{"from":"12","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","notes"],"removed":[]},"status":"changed","synopsis":null,"to":"13"},{"from":"13","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","notes","examples","compatibility"],"removed":[]},"status":"changed","synopsis":{"added":["CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }"],"removed":[]},"to":"14"},{"from":"15","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description"],"removed":[]},"status":"changed","synopsis":null,"to":"16"},{"from":"16","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["description","notes","examples"],"removed":[]},"status":"changed","synopsis":null,"to":"17"},{"from":"18","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":["parameters","notes"],"removed":[]},"status":"changed","synopsis":{"added":["[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ] [ ENFORCED ]"],"removed":[]},"to":"19"}],"content_hash":"5d15ebfe5b72ca63758d5d0f8e31ce2b2571693b4749c9e442a0cbd62d1a4951","editorial":{},"first_version":"6.4","group":"trigger","imported_at":"2026-09-30T17:43:38.423203+08:00","last_version":"20","name":"CREATE TRIGGER","object":"TRIGGER","position":6007,"present_in":["6.4","6.5","7.0","7.1","7.2","7.3","7.4","8.0","8.1","8.2","8.3","8.4","9.0","9.1","9.2","9.3","9.4","9.5","9.6","10","11","12","13","14","15","16","17","18","19","20"],"purpose":"define a new trigger","purpose_zh":"","related":["alter-trigger","drop-trigger","create-function","set-constraints"],"slug":"create-trigger","source_rev":"a709ab85","synopsis":"CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }\nON table_name\n[ FROM referenced_table_name ]\n[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ] [ ENFORCED ]\n[ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]\n[ FOR [ EACH ] { ROW | STATEMENT } ]\n[ WHEN ( condition ) ]\nEXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )\n\nwhere event can be one of:\n\nINSERT\nUPDATE [ OF column_name [, ... ] ]\nDELETE\nTRUNCATE","verb":"CREATE"}},"Definition":{"Collection":"sql","Key":"create-trigger","SourceDatabase":"center","Version":"18","SourceTable":"sqlcmd","SourceKey":"create-trigger","SourceRevision":"a709ab85","Facts":{"anchor":"SQL-CREATETRIGGER","file":"sql-createtrigger.html","lang":"en","name":"CREATE TRIGGER","purpose":"define a new trigger","purpose_zh":"","related":["alter-trigger","drop-trigger","create-function","set-constraints"],"sections":[{"html":"\u003cp\u003e\u003ccode class=\"command\"\u003eCREATE TRIGGER\u003c/code\u003e creates a new trigger. \u003ccode class=\"command\"\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e will either create a new trigger, or replace an existing trigger. The trigger will be associated with the specified table, view, or foreign table and will execute the specified function \u003cem class=\"replaceable\"\u003e\u003ccode\u003efunction_name\u003c/code\u003e\u003c/em\u003e when certain operations are performed on that table.\u003c/p\u003e\u003cp\u003eTo replace the current definition of an existing trigger, use \u003ccode class=\"command\"\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e, specifying the existing trigger's name and parent table. All other properties are replaced.\u003c/p\u003e\u003cp\u003eThe trigger can be specified to fire before the operation is attempted on a row (before constraints are checked and the \u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e, \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e, or \u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e is attempted); or after the operation has completed (after constraints are checked and the \u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e, \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e, or \u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e has completed); or instead of the operation (in the case of inserts, updates or deletes on a view). If the trigger fires before or instead of the event, the trigger can skip the operation for the current row, or change the row being inserted (for \u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e and \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e operations only). If the trigger fires after the event, all changes, including the effects of other triggers, are \u003cspan class=\"quote\"\u003e“\u003cspan class=\"quote\"\u003evisible\u003c/span\u003e”\u003c/span\u003e to the trigger.\u003c/p\u003e\u003cp\u003eA trigger that is marked \u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e is called once for every row that the operation modifies. For example, a \u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e that affects 10 rows will cause any \u003ccode class=\"literal\"\u003eON DELETE\u003c/code\u003e triggers on the target relation to be called 10 separate times, once for each deleted row. In contrast, a trigger that is marked \u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e only executes once for any given operation, regardless of how many rows it modifies (in particular, an operation that modifies zero rows will still result in the execution of any applicable \u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e triggers).\u003c/p\u003e\u003cp\u003eTriggers that are specified to fire \u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e the trigger event must be marked \u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e, and can only be defined on views. \u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e and \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e triggers on a view must be marked as \u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e.\u003c/p\u003e\u003cp\u003eIn addition, triggers may be defined to fire for \u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e, though only \u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e.\u003c/p\u003e\u003cp\u003eThe following table summarizes which types of triggers may be used on tables, views, and foreign tables:\u003c/p\u003e\u003cdiv class=\"informaltable\"\u003e\u003ctable class=\"informaltable\"\u003e\u003cthead\u003e\u003ctr\u003e\u003cth\u003eWhen\u003c/th\u003e\u003cth\u003eEvent\u003c/th\u003e\u003cth\u003eRow-level\u003c/th\u003e\u003cth\u003eStatement-level\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e/\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e/\u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003eTables and foreign tables\u003c/td\u003e\u003ctd\u003eTables, views, and foreign tables\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003eTables and foreign tables\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e/\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e/\u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003eTables and foreign tables\u003c/td\u003e\u003ctd\u003eTables, views, and foreign tables\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003eTables and foreign tables\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e/\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e/\u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003eViews\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e\u003c/div\u003e\u003cp\u003eAlso, a trigger definition can specify a Boolean \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e condition, which will be tested to see whether the trigger should be fired. In row-level triggers the \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e condition can examine the old and/or new values of columns of the row. Statement-level triggers can also have \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e conditions, although the feature is not so useful for them since the condition cannot refer to any values in the table.\u003c/p\u003e\u003cp\u003eIf multiple triggers of the same kind are defined for the same event, they will be fired in alphabetical order by name.\u003c/p\u003e\u003cp\u003eWhen the \u003ccode class=\"literal\"\u003eCONSTRAINT\u003c/code\u003e option is specified, this command creates a \u003cem class=\"firstterm\"\u003econstraint trigger\u003c/em\u003e. This is the same as a regular trigger except that the timing of the trigger firing can be adjusted using \u003ca href=\"/docs/18/sql-set-constraints.html\" title=\"SET CONSTRAINTS\"\u003e\u003ccode class=\"command\"\u003eSET CONSTRAINTS\u003c/code\u003e\u003c/a\u003e. Constraint triggers must be \u003ccode class=\"literal\"\u003eAFTER ROW\u003c/code\u003e triggers on plain tables (not foreign tables). They can be fired either at the end of the statement causing the triggering event, or at the end of the containing transaction; in the latter case they are said to be \u003cem class=\"firstterm\"\u003edeferred\u003c/em\u003e. A pending deferred-trigger firing can also be forced to happen immediately by using \u003ccode class=\"command\"\u003eSET CONSTRAINTS\u003c/code\u003e. Constraint triggers are expected to raise an exception when the constraints they implement are violated.\u003c/p\u003e\u003cp\u003eThe \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e option enables collection of \u003cem class=\"firstterm\"\u003etransition relations\u003c/em\u003e, which are row sets that include all of the rows inserted, deleted, or modified by the current SQL statement. This feature lets the trigger see a global view of what the statement did, not just one row at a time. This option is only allowed for an \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e trigger on a plain table (not a foreign table). The trigger should not be a constraint trigger. Also, if the trigger is an \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e trigger, it must not specify a \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e list when using this option. \u003ccode class=\"literal\"\u003eOLD TABLE\u003c/code\u003e may only be specified once, and only for a trigger that can fire on \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e or \u003ccode class=\"literal\"\u003eDELETE\u003c/code\u003e; it creates a transition relation containing the \u003cem class=\"firstterm\"\u003ebefore-images\u003c/em\u003e of all rows updated or deleted by the statement. Similarly, \u003ccode class=\"literal\"\u003eNEW TABLE\u003c/code\u003e may only be specified once, and only for a trigger that can fire on \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e or \u003ccode class=\"literal\"\u003eINSERT\u003c/code\u003e; it creates a transition relation containing the \u003cem class=\"firstterm\"\u003eafter-images\u003c/em\u003e of all rows updated or inserted by the statement.\u003c/p\u003e\u003cp\u003e\u003ccode class=\"command\"\u003eSELECT\u003c/code\u003e does not modify any rows so you cannot create \u003ccode class=\"command\"\u003eSELECT\u003c/code\u003e triggers. Rules and views may provide workable solutions to problems that seem to need \u003ccode class=\"command\"\u003eSELECT\u003c/code\u003e triggers.\u003c/p\u003e\u003cp\u003eRefer to \u003ca href=\"/docs/18/triggers.html\" title=\"Chapter 37. Triggers\"\u003eChapter 37\u003c/a\u003e for more information about triggers.\u003c/p\u003e","key":"description","title":"Description"},{"html":"\u003cdiv class=\"variablelist\"\u003e\u003cdl class=\"variablelist\"\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eThe name to give the new trigger. This must be distinct from the name of any other trigger for the same table. The name cannot be schema-qualified — the trigger inherits the schema of its table. For a constraint trigger, this is also the name to use when modifying the trigger's behavior using \u003ccode class=\"command\"\u003eSET CONSTRAINTS\u003c/code\u003e.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eDetermines whether the function is called before, after, or instead of the event. A constraint trigger can only be specified as \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003eevent\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eOne of \u003ccode class=\"literal\"\u003eINSERT\u003c/code\u003e, \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e, \u003ccode class=\"literal\"\u003eDELETE\u003c/code\u003e, or \u003ccode class=\"literal\"\u003eTRUNCATE\u003c/code\u003e; this specifies the event that will fire the trigger. Multiple events can be specified using \u003ccode class=\"literal\"\u003eOR\u003c/code\u003e, except when transition relations are requested.\u003c/p\u003e\u003cp\u003eFor \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e events, it is possible to specify a list of columns using this syntax:\u003c/p\u003e\u003cpre class=\"synopsis\"\u003eUPDATE OF \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name1\u003c/code\u003e\u003c/em\u003e [, \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name2\u003c/code\u003e\u003c/em\u003e ... ]\n\u003c/pre\u003e\u003cp\u003eThe trigger will only fire if at least one of the listed columns is mentioned as a target of the \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e command or if one of the listed columns is a generated column that depends on a column that is the target of the \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e.\u003c/p\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003eINSTEAD OF UPDATE\u003c/code\u003e events do not allow a list of columns. A column list cannot be specified when requesting transition relations, either.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003etable_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eThe name (optionally schema-qualified) of the table, view, or foreign table the trigger is for.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003ereferenced_table_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eThe (possibly schema-qualified) name of another table referenced by the constraint. This option is used for foreign-key constraints and is not recommended for general use. This can only be specified for constraint triggers.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eDEFERRABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eNOT DEFERRABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eINITIALLY IMMEDIATE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eINITIALLY DEFERRED\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eThe default timing of the trigger. See the \u003ca href=\"/docs/18/sql-createtable.html\" title=\"CREATE TABLE\"\u003e\u003cspan class=\"refentrytitle\"\u003eCREATE TABLE\u003c/span\u003e\u003c/a\u003e documentation for details of these constraint options. This can only be specified for constraint triggers.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eThis keyword immediately precedes the declaration of one or two relation names that provide access to the transition relations of the triggering statement.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eOLD TABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eNEW TABLE\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eThis clause indicates whether the following relation name is for the before-image transition relation or the after-image transition relation.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003etransition_relation_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eThe (unqualified) name to be used within the trigger for this transition relation.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eThis specifies whether the trigger function should be fired once for every row affected by the trigger event, or just once per SQL statement. If neither is specified, \u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e is the default. Constraint triggers can only be specified \u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003econdition\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eA Boolean expression that determines whether the trigger function will actually be executed. If \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e is specified, the function will only be called if the \u003cem class=\"replaceable\"\u003e\u003ccode\u003econdition\u003c/code\u003e\u003c/em\u003e returns \u003ccode class=\"literal\"\u003etrue\u003c/code\u003e. In \u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e triggers, the \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e condition can refer to columns of the old and/or new row values by writing \u003ccode class=\"literal\"\u003eOLD.\u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e or \u003ccode class=\"literal\"\u003eNEW.\u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e respectively. Of course, \u003ccode class=\"literal\"\u003eINSERT\u003c/code\u003e triggers cannot refer to \u003ccode class=\"literal\"\u003eOLD\u003c/code\u003e and \u003ccode class=\"literal\"\u003eDELETE\u003c/code\u003e triggers cannot refer to \u003ccode class=\"literal\"\u003eNEW\u003c/code\u003e.\u003c/p\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e triggers do not support \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e conditions.\u003c/p\u003e\u003cp\u003eCurrently, \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e expressions cannot contain subqueries.\u003c/p\u003e\u003cp\u003eNote that for constraint triggers, evaluation of the \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e condition is not deferred, but occurs immediately after the row update operation is performed. If the condition does not evaluate to true then the trigger is not queued for deferred execution.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003efunction_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eA user-supplied function that is declared as taking no arguments and returning type \u003ccode class=\"literal\"\u003etrigger\u003c/code\u003e, which is executed when the trigger fires.\u003c/p\u003e\u003cp\u003eIn the syntax of \u003ccode class=\"literal\"\u003eCREATE TRIGGER\u003c/code\u003e, the keywords \u003ccode class=\"literal\"\u003eFUNCTION\u003c/code\u003e and \u003ccode class=\"literal\"\u003ePROCEDURE\u003c/code\u003e are equivalent, but the referenced function must in any case be a function, not a procedure. The use of the keyword \u003ccode class=\"literal\"\u003ePROCEDURE\u003c/code\u003e here is historical and deprecated.\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003earguments\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003eAn optional comma-separated list of arguments to be provided to the function when the trigger is executed. The arguments are literal string constants. Simple names and numeric constants can be written here, too, but they will all be converted to strings. Please check the description of the implementation language of the trigger function to find out how these arguments can be accessed within the function; it might be different from normal function arguments.\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e","key":"parameters","title":"Parameters"},{"html":"\u003cp\u003eTo create or replace a trigger on a table, the user must have the \u003ccode class=\"literal\"\u003eTRIGGER\u003c/code\u003e privilege on the table. The user must also have \u003ccode class=\"literal\"\u003eEXECUTE\u003c/code\u003e privilege on the trigger function.\u003c/p\u003e\u003cp\u003eUse \u003ca href=\"/docs/18/sql-droptrigger.html\" title=\"DROP TRIGGER\"\u003e\u003ccode class=\"command\"\u003eDROP TRIGGER\u003c/code\u003e\u003c/a\u003e to remove a trigger.\u003c/p\u003e\u003cp\u003eCreating a row-level trigger on a partitioned table will cause an identical \u003cspan class=\"quote\"\u003e“\u003cspan class=\"quote\"\u003eclone\u003c/span\u003e”\u003c/span\u003e trigger to be created on each of its existing partitions; and any partitions created or attached later will have an identical trigger, too. If there is a conflictingly-named trigger on a child partition already, an error occurs unless \u003ccode class=\"command\"\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e is used, in which case that trigger is replaced with a clone trigger. When a partition is detached from its parent, its clone triggers are removed.\u003c/p\u003e\u003cp\u003eA column-specific trigger (one defined using the \u003ccode class=\"literal\"\u003eUPDATE OF \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e syntax) will fire when any of its columns are listed as targets in the \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e command's \u003ccode class=\"literal\"\u003eSET\u003c/code\u003e list. It is possible for a column's value to change even when the trigger is not fired, because changes made to the row's contents by \u003ccode class=\"literal\"\u003eBEFORE UPDATE\u003c/code\u003e triggers are not considered. Conversely, a command such as \u003ccode class=\"literal\"\u003eUPDATE ... SET x = x ...\u003c/code\u003e will fire a trigger on column \u003ccode class=\"literal\"\u003ex\u003c/code\u003e, even though the column's value did not change.\u003c/p\u003e\u003cp\u003eIn a \u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e trigger, the \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e condition is evaluated just before the function is or would be executed, so using \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e is not materially different from testing the same condition at the beginning of the trigger function. Note in particular that the \u003ccode class=\"literal\"\u003eNEW\u003c/code\u003e row seen by the condition is the current value, as possibly modified by earlier triggers. Also, a \u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e trigger's \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e condition is not allowed to examine the system columns of the \u003ccode class=\"literal\"\u003eNEW\u003c/code\u003e row (such as \u003ccode class=\"literal\"\u003ectid\u003c/code\u003e), because those won't have been set yet.\u003c/p\u003e\u003cp\u003eIn an \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e trigger, the \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e condition is evaluated just after the row update occurs, and it determines whether an event is queued to fire the trigger at the end of statement. So when an \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e trigger's \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e condition does not return true, it is not necessary to queue an event nor to re-fetch the row at end of statement. This can result in significant speedups in statements that modify many rows, if the trigger only needs to be fired for a few of the rows.\u003c/p\u003e\u003cp\u003eIn some cases it is possible for a single SQL command to fire more than one kind of trigger. For instance an \u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e with an \u003ccode class=\"literal\"\u003eON CONFLICT DO UPDATE\u003c/code\u003e clause may cause both insert and update operations, so it will fire both kinds of triggers as needed. The transition relations supplied to triggers are specific to their event type; thus an \u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e trigger will see only the inserted rows, while an \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e trigger will see only the updated rows.\u003c/p\u003e\u003cp\u003eRow updates or deletions caused by foreign-key enforcement actions, such as \u003ccode class=\"literal\"\u003eON UPDATE CASCADE\u003c/code\u003e or \u003ccode class=\"literal\"\u003eON DELETE SET NULL\u003c/code\u003e, are treated as part of the SQL command that caused them (note that such actions are never deferred). Relevant triggers on the affected table will be fired, so that this provides another way in which an SQL command might fire triggers not directly matching its type. In simple cases, triggers that request transition relations will see all changes caused in their table by a single original SQL command as a single transition relation. However, there are cases in which the presence of an \u003ccode class=\"literal\"\u003eAFTER ROW\u003c/code\u003e trigger that requests transition relations will cause the foreign-key enforcement actions triggered by a single SQL command to be split into multiple steps, each with its own transition relation(s). In such cases, any statement-level triggers that are present will be fired once per creation of a transition relation set, ensuring that the triggers see each affected row in a transition relation once and only once.\u003c/p\u003e\u003cp\u003eStatement-level triggers on a view are fired only if the action on the view is handled by a row-level \u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e trigger. If the action is handled by an \u003ccode class=\"literal\"\u003eINSTEAD\u003c/code\u003e rule, then whatever statements are emitted by the rule are executed in place of the original statement naming the view, so that the triggers that will be fired are those on tables named in the replacement statements. Similarly, if the view is automatically updatable, then the action is handled by automatically rewriting the statement into an action on the view's base table, so that the base table's statement-level triggers are the ones that are fired.\u003c/p\u003e\u003cp\u003eModifying a partitioned table or a table with inheritance children fires statement-level triggers attached to the explicitly named table, but not statement-level triggers for its partitions or child tables. In contrast, row-level triggers are fired on the rows in affected partitions or child tables, even if they are not explicitly named in the query. If a statement-level trigger has been defined with transition relations named by a \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e clause, then before and after images of rows are visible from all affected partitions or child tables. In the case of inheritance children, the row images include only columns that are present in the table that the trigger is attached to.\u003c/p\u003e\u003cp\u003eCurrently, row-level triggers with transition relations cannot be defined on partitions or inheritance child tables. Also, triggers on partitioned tables may not be \u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e.\u003c/p\u003e\u003cp\u003eCurrently, the \u003ccode class=\"literal\"\u003eOR REPLACE\u003c/code\u003e option is not supported for constraint triggers.\u003c/p\u003e\u003cp\u003eReplacing an existing trigger within a transaction that has already performed updating actions on the trigger's table is not recommended. Trigger firing decisions, or portions of firing decisions, that have already been made will not be reconsidered, so the effects could be surprising.\u003c/p\u003e\u003cp\u003eThere are a few built-in trigger functions that can be used to solve common problems without having to write your own trigger code; see \u003ca href=\"/docs/18/functions-trigger.html\" title=\"9.29. Trigger Functions\"\u003eSection 9.29\u003c/a\u003e.\u003c/p\u003e","key":"notes","title":"Notes"},{"html":"\u003cp\u003eExecute the function \u003ccode class=\"function\"\u003echeck_account_update\u003c/code\u003e whenever a row of the table \u003ccode class=\"literal\"\u003eaccounts\u003c/code\u003e is about to be updated:\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER check_update\n    BEFORE UPDATE ON accounts\n    FOR EACH ROW\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003eModify that trigger definition to only execute the function if column \u003ccode class=\"literal\"\u003ebalance\u003c/code\u003e is specified as a target in the \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e command:\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE OR REPLACE TRIGGER check_update\n    BEFORE UPDATE OF balance ON accounts\n    FOR EACH ROW\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003eThis form only executes the function if column \u003ccode class=\"literal\"\u003ebalance\u003c/code\u003e has in fact changed value:\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER check_update\n    BEFORE UPDATE ON accounts\n    FOR EACH ROW\n    WHEN (OLD.balance IS DISTINCT FROM NEW.balance)\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003eCall a function to log updates of \u003ccode class=\"literal\"\u003eaccounts\u003c/code\u003e, but only if something changed:\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER log_update\n    AFTER UPDATE ON accounts\n    FOR EACH ROW\n    WHEN (OLD.* IS DISTINCT FROM NEW.*)\n    EXECUTE FUNCTION log_account_update();\n\u003c/pre\u003e\u003cp\u003eExecute the function \u003ccode class=\"function\"\u003eview_insert_row\u003c/code\u003e for each row to insert rows into the tables underlying a view:\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER view_insert\n    INSTEAD OF INSERT ON my_view\n    FOR EACH ROW\n    EXECUTE FUNCTION view_insert_row();\n\u003c/pre\u003e\u003cp\u003eExecute the function \u003ccode class=\"function\"\u003echeck_transfer_balances_to_zero\u003c/code\u003e for each statement to confirm that the \u003ccode class=\"literal\"\u003etransfer\u003c/code\u003e rows offset to a net of zero:\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER transfer_insert\n    AFTER INSERT ON transfer\n    REFERENCING NEW TABLE AS inserted\n    FOR EACH STATEMENT\n    EXECUTE FUNCTION check_transfer_balances_to_zero();\n\u003c/pre\u003e\u003cp\u003eExecute the function \u003ccode class=\"function\"\u003echeck_matching_pairs\u003c/code\u003e for each row to confirm that changes are made to matching pairs at the same time (by the same statement):\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER paired_items_update\n    AFTER UPDATE ON paired_items\n    REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab\n    FOR EACH ROW\n    EXECUTE FUNCTION check_matching_pairs();\n\u003c/pre\u003e\u003cp\u003e\u003ca href=\"/docs/18/trigger-example.html\" title=\"37.4. A Complete Trigger Example\"\u003eSection 37.4\u003c/a\u003e contains a complete example of a trigger function written in C.\u003c/p\u003e","key":"examples","title":"Examples"},{"html":"\u003cp\u003eThe \u003ccode class=\"command\"\u003eCREATE TRIGGER\u003c/code\u003e statement in \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e implements a subset of the \u003cacronym\u003eSQL\u003c/acronym\u003e standard. The following functionalities are currently missing:\u003c/p\u003e\u003cdiv class=\"itemizedlist\"\u003e\u003cul class=\"itemizedlist\"\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003eWhile transition table names for \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e triggers are specified using the \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e clause in the standard way, the row variables used in \u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e triggers may not be specified in a \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e clause. They are available in a manner that is dependent on the language in which the trigger function is written, but is fixed for any one language. Some languages effectively behave as though there is a \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e clause containing \u003ccode class=\"literal\"\u003eOLD ROW AS OLD NEW ROW AS NEW\u003c/code\u003e.\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003eThe standard allows transition tables to be used with column-specific \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e triggers, but then the set of rows that should be visible in the transition tables depends on the trigger's column list. This is not currently implemented by \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e.\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e only allows the execution of a user-defined function for the triggered action. The standard allows the execution of a number of other SQL commands, such as \u003ccode class=\"command\"\u003eCREATE TABLE\u003c/code\u003e, as the triggered action. This limitation is not hard to work around by creating a user-defined function that executes the desired commands.\u003c/p\u003e\u003c/li\u003e\u003c/ul\u003e\u003c/div\u003e\u003cp\u003eSQL specifies that multiple triggers should be fired in time-of-creation order. \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e uses name order, which was judged to be more convenient.\u003c/p\u003e\u003cp\u003eSQL specifies that \u003ccode class=\"literal\"\u003eBEFORE DELETE\u003c/code\u003e triggers on cascaded deletes fire \u003cspan class=\"emphasis\"\u003e\u003cem\u003eafter\u003c/em\u003e\u003c/span\u003e the cascaded \u003ccode class=\"literal\"\u003eDELETE\u003c/code\u003e completes. The \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e behavior is for \u003ccode class=\"literal\"\u003eBEFORE DELETE\u003c/code\u003e to always fire before the delete action, even a cascading one. This is considered more consistent. There is also nonstandard behavior if \u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e triggers modify rows or prevent updates during an update that is caused by a referential action. This can lead to constraint violations or stored data that does not honor the referential constraint.\u003c/p\u003e\u003cp\u003eThe ability to specify multiple actions for a single trigger using \u003ccode class=\"literal\"\u003eOR\u003c/code\u003e is a \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e extension of the SQL standard.\u003c/p\u003e\u003cp\u003eThe ability to fire triggers for \u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e is a \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e extension of the SQL standard, as is the ability to define statement-level triggers on views.\u003c/p\u003e\u003cp\u003e\u003ccode class=\"command\"\u003eCREATE CONSTRAINT TRIGGER\u003c/code\u003e is a \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e extension of the \u003cacronym\u003eSQL\u003c/acronym\u003e standard. So is the \u003ccode class=\"literal\"\u003eOR REPLACE\u003c/code\u003e option.\u003c/p\u003e","key":"compatibility","title":"Compatibility"},{"html":"\u003cspan class=\"simplelist\"\u003e\u003ca href=\"/wiki/sql/alter-trigger/?v=18\" title=\"ALTER TRIGGER\"\u003e\u003cspan class=\"refentrytitle\"\u003eALTER TRIGGER\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/drop-trigger/?v=18\" title=\"DROP TRIGGER\"\u003e\u003cspan class=\"refentrytitle\"\u003eDROP TRIGGER\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/create-function/?v=18\" title=\"CREATE FUNCTION\"\u003e\u003cspan class=\"refentrytitle\"\u003eCREATE FUNCTION\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/set-constraints/?v=18\" title=\"SET CONSTRAINTS\"\u003e\u003cspan class=\"refentrytitle\"\u003eSET CONSTRAINTS\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e","key":"see_also","title":"See Also"}],"sections_same_as":"","slug":"18","synopsis_html":"CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER \u003cem class=\"replaceable\"\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/em\u003e { BEFORE | AFTER | INSTEAD OF } { \u003cem class=\"replaceable\"\u003e\u003ccode\u003eevent\u003c/code\u003e\u003c/em\u003e [ OR ... ] }\n    ON \u003cem class=\"replaceable\"\u003e\u003ccode\u003etable_name\u003c/code\u003e\u003c/em\u003e\n    [ FROM \u003cem class=\"replaceable\"\u003e\u003ccode\u003ereferenced_table_name\u003c/code\u003e\u003c/em\u003e ]\n    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]\n    [ REFERENCING { { OLD | NEW } TABLE [ AS ] \u003cem class=\"replaceable\"\u003e\u003ccode\u003etransition_relation_name\u003c/code\u003e\u003c/em\u003e } [ ... ] ]\n    [ FOR [ EACH ] { ROW | STATEMENT } ]\n    [ WHEN ( \u003cem class=\"replaceable\"\u003e\u003ccode\u003econdition\u003c/code\u003e\u003c/em\u003e ) ]\n    EXECUTE { FUNCTION | PROCEDURE } \u003cem class=\"replaceable\"\u003e\u003ccode\u003efunction_name\u003c/code\u003e\u003c/em\u003e ( \u003cem class=\"replaceable\"\u003e\u003ccode\u003earguments\u003c/code\u003e\u003c/em\u003e )\n\n\u003cspan class=\"phrase\"\u003ewhere \u003cem class=\"replaceable\"\u003e\u003ccode\u003eevent\u003c/code\u003e\u003c/em\u003e can be one of:\u003c/span\u003e\n\n    INSERT\n    UPDATE [ OF \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e [, ... ] ]\n    DELETE\n    TRUNCATE","synopsis_text":"CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }\nON table_name\n[ FROM referenced_table_name ]\n[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]\n[ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]\n[ FOR [ EACH ] { ROW | STATEMENT } ]\n[ WHEN ( condition ) ]\nEXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )\n\nwhere event can be one of:\n\nINSERT\nUPDATE [ OF column_name [, ... ] ]\nDELETE\nTRUNCATE"},"ManualEvidence":{},"MeasuredEvidence":{}},"Text":{"Collection":"sql","Key":"create-trigger","SourceDatabase":"pgweb","Version":"18","Locale":"zh-Hans","Title":"CREATE TRIGGER","Summary":"定义一个新触发器","BodyHTML":"\u003cpre\u003eCREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }\nON table_name\n[ FROM referenced_table_name ]\n[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]\n[ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]\n[ FOR [ EACH ] { ROW | STATEMENT } ]\n[ WHEN ( condition ) ]\nEXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )\n\n其中 event 可以是以下之一：\n\nINSERT\nUPDATE [ OF column_name [, ... ] ]\nDELETE\nTRUNCATE\u003c/pre\u003e\u003csection\u003e\u003ch2\u003e描述\u003c/h2\u003e\u003cp\u003e\u003ccode\u003eCREATE TRIGGER\u003c/code\u003e创建一个新触发器。\u003ccode\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e要么创建一个新触发器，要么替换一个现有触发器。该触发器会关联到指定的表、视图或外部表，并在对该对象执行某些操作时执行指定的函数 \u003cem\u003e\u003ccode\u003efunction_name\u003c/code\u003e\u003c/em\u003e。\u003c/p\u003e\u003cp\u003e要替换现有触发器的当前定义，可使用 \u003ccode\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e，并指定现有触发器的名称及其所属表。其他所有属性都会被替换。\u003c/p\u003e\u003cp\u003e可以指定触发器在尝试对某一行执行该操作之前引发（即在检查约束以及尝试执行\u003ccode\u003eINSERT\u003c/code\u003e、\u003ccode\u003eUPDATE\u003c/code\u003e或\u003ccode\u003eDELETE\u003c/code\u003e之前）；也可以在该操作完成之后引发（即在检查约束以及完成 \u003ccode\u003eINSERT\u003c/code\u003e、\u003ccode\u003eUPDATE\u003c/code\u003e或 \u003ccode\u003eDELETE\u003c/code\u003e之后）；或者取代该操作执行（用于视图上的插入、更新或删除）。如果触发器在事件之前引发，或者改为取代该事件执行，则它可以跳过对当前行的操作，或者修改待插入的行（仅适用于\u003ccode\u003eINSERT\u003c/code\u003e和\u003ccode\u003eUPDATE\u003c/code\u003e操作）。如果触发器在事件之后引发，则所有更改（包括其他触发器的影响）对该触发器都是\u003cspan\u003e“\u003cspan\u003e可见\u003c/span\u003e”\u003c/span\u003e的。\u003c/p\u003e\u003cp\u003e被标记为\u003ccode\u003eFOR EACH ROW\u003c/code\u003e的触发器会针对该操作修改的每一行调用一次。例如，一个影响 10 行的\u003ccode\u003eDELETE\u003c/code\u003e会导致目标关系上的 \u003ccode\u003eON DELETE\u003c/code\u003e触发器分别被调用 10 次，即每个被删除的行调用一次。相比之下，被标记为\u003ccode\u003eFOR EACH STATEMENT\u003c/code\u003e的触发器对于任意给定操作只执行一次，不管该操作修改了多少行（特别是，修改零行的操作仍会导致所有适用的 \u003ccode\u003eFOR EACH STATEMENT\u003c/code\u003e触发器执行）。\u003c/p\u003e\u003cp\u003e被指定为取代该触发事件执行的 \u003ccode\u003eINSTEAD OF\u003c/code\u003e 触发器必须标记为 \u003ccode\u003eFOR EACH ROW\u003c/code\u003e，并且只能定义在视图上。视图上的 \u003ccode\u003eBEFORE\u003c/code\u003e 和 \u003ccode\u003eAFTER\u003c/code\u003e 触发器必须标记为 \u003ccode\u003eFOR EACH STATEMENT\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e此外，也可以把触发器定义为在 \u003ccode\u003eTRUNCATE\u003c/code\u003e 时引发，但只能是 \u003ccode\u003eFOR EACH STATEMENT\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e下表汇总了哪些类型的触发器可用于表、视图和外部表：\u003c/p\u003e\u003cdiv\u003e\u003ctable\u003e\u003cthead\u003e\u003ctr\u003e\u003cth\u003e时机\u003c/th\u003e\u003cth\u003e事件\u003c/th\u003e\u003cth\u003e行级\u003c/th\u003e\u003cth\u003e语句级\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode\u003eBEFORE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003eINSERT\u003c/code\u003e/\u003ccode\u003eUPDATE\u003c/code\u003e/\u003ccode\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e表和外部表\u003c/td\u003e\u003ctd\u003e表、视图和外部表\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003e表和外部表\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode\u003eAFTER\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003eINSERT\u003c/code\u003e/\u003ccode\u003eUPDATE\u003c/code\u003e/\u003ccode\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e表和外部表\u003c/td\u003e\u003ctd\u003e表、视图和外部表\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003e表和外部表\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode\u003eINSTEAD OF\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode\u003eINSERT\u003c/code\u003e/\u003ccode\u003eUPDATE\u003c/code\u003e/\u003ccode\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e视图\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e\u003c/div\u003e\u003cp\u003e此外，触发器定义还可以指定一个布尔型 \u003ccode\u003eWHEN\u003c/code\u003e 条件，用于测试是否应当引发该触发器。在行级触发器中，\u003ccode\u003eWHEN\u003c/code\u003e 条件可以检查该行各列的新值和/或旧值。语句级触发器也可以带有 \u003ccode\u003eWHEN\u003c/code\u003e 条件，不过这一特性对它们的用处不大，因为该条件无法引用表中的任何值。\u003c/p\u003e\u003cp\u003e如果针对同一事件定义了多个同类触发器，它们将按名称的字母顺序引发。\u003c/p\u003e\u003cp\u003e当指定\u003ccode\u003eCONSTRAINT\u003c/code\u003e选项时，此命令会创建一个\u003cem\u003e约束触发器\u003c/em\u003e。这与普通触发器相同，只是可以使用 \u003ca href=\"/docs/18/sql-set-constraints.html\" title=\"SET CONSTRAINTS\" rel=\"nofollow\"\u003e\u003ccode\u003eSET CONSTRAINTS\u003c/code\u003e\u003c/a\u003e 调整其引发时机。约束触发器必须是普通表（不是外部表）上的 \u003ccode\u003eAFTER ROW\u003c/code\u003e 触发器。它们既可以在导致触发事件的语句结束时引发，也可以在包含它的事务结束时引发；后一种情况称为\u003cem\u003e延迟\u003c/em\u003e。也可以使用\u003ccode\u003eSET CONSTRAINTS\u003c/code\u003e强制尚待执行的延迟触发器立即引发。约束触发器应当在其所实现的约束遭到违反时抛出异常。\u003c/p\u003e\u003cp\u003e\u003ccode\u003eREFERENCING\u003c/code\u003e选项允许收集\u003cem\u003e过渡关系\u003c/em\u003e，即包含当前 SQL 语句插入、删除或修改的全部行的行集合。该特性使触发器能够看到该语句所做更改的整体视图，而不是一次只看到一行。该选项仅允许用于普通表（不是外部表）上的 \u003ccode\u003eAFTER\u003c/code\u003e 触发器。该触发器还不能是约束触发器。此外，如果触发器是 \u003ccode\u003eUPDATE\u003c/code\u003e 触发器，则在使用该选项时不得指定 \u003cem\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e 列表。\u003ccode\u003eOLD TABLE\u003c/code\u003e 最多只能指定一次，并且只能用于可能在 \u003ccode\u003eUPDATE\u003c/code\u003e 或 \u003ccode\u003eDELETE\u003c/code\u003e 上引发的触发器；它会创建一个过渡关系，其中包含该语句更新或删除的全部行的\u003cem\u003e前映像\u003c/em\u003e。同样，\u003ccode\u003eNEW TABLE\u003c/code\u003e 最多只能指定一次，并且只能用于可能在 \u003ccode\u003eUPDATE\u003c/code\u003e 或 \u003ccode\u003eINSERT\u003c/code\u003e 上引发的触发器；它会创建一个过渡关系，其中包含该语句更新或插入的全部行的\u003cem\u003e后映像\u003c/em\u003e。\u003c/p\u003e\u003cp\u003e\u003ccode\u003eSELECT\u003c/code\u003e 不会修改任何行，因此无法创建 \u003ccode\u003eSELECT\u003c/code\u003e 触发器。对于那些看起来需要 \u003ccode\u003eSELECT\u003c/code\u003e 触发器的问题，规则和视图可能提供可行的解决方案。\u003c/p\u003e\u003cp\u003e参见\u003ca href=\"/docs/18/triggers.html\" rel=\"nofollow\"\u003e第 37 章\u003c/a\u003e以了解有关触发器的更多信息。\u003c/p\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e参数\u003c/h2\u003e\u003cdiv\u003e\u003cdl\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e新触发器的名称。它必须不同于同一表上的任何其他触发器名称。该名称不能是模式限定名 — 触发器会继承其所属表的模式。对于约束触发器，这也是使用\u003ccode\u003eSET CONSTRAINTS\u003c/code\u003e 修改触发器行为时所使用的名称。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eBEFORE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ccode\u003eAFTER\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ccode\u003eINSTEAD OF\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e决定该函数是在事件之前、之后调用，还是取代该事件执行。约束触发器只能指定为\u003ccode\u003eAFTER\u003c/code\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003eevent\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e\u003ccode\u003eINSERT\u003c/code\u003e、\u003ccode\u003eUPDATE\u003c/code\u003e、\u003ccode\u003eDELETE\u003c/code\u003e或 \u003ccode\u003eTRUNCATE\u003c/code\u003e 之一；它指定了哪个事件将引发该触发器。除请求过渡关系的情况外，可以使用\u003ccode\u003eOR\u003c/code\u003e指定多个事件。\u003c/p\u003e\u003cp\u003e对于\u003ccode\u003eUPDATE\u003c/code\u003e事件，可以使用以下语法指定列列表：\u003c/p\u003e\u003cpre\u003eUPDATE OF \u003cem\u003e\u003ccode\u003ecolumn_name1\u003c/code\u003e\u003c/em\u003e [, \u003cem\u003e\u003ccode\u003ecolumn_name2\u003c/code\u003e\u003c/em\u003e ... ]\n\u003c/pre\u003e\u003cp\u003e只有当列出的列中至少有一列被列为\u003ccode\u003eUPDATE\u003c/code\u003e命令的目标列时，或者列出的列中有某个生成列依赖于作为\u003ccode\u003eUPDATE\u003c/code\u003e目标的列时，该触发器才会引发。\u003c/p\u003e\u003cp\u003e\u003ccode\u003eINSTEAD OF UPDATE\u003c/code\u003e 事件不允许指定列列表。请求过渡关系时也不能指定列列表。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003etable_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e该触发器所作用的表、视图或外部表的名称（可以是模式限定的）。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003ereferenced_table_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e约束所引用的另一张表的名称（可以是模式限定的）。此选项用于外键约束，不建议用于一般用途。它只能为约束触发器指定。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eDEFERRABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ccode\u003eNOT DEFERRABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ccode\u003eINITIALLY IMMEDIATE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ccode\u003eINITIALLY DEFERRED\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e触发器的默认时机。有关这些约束选项的细节，请参见\u003ca href=\"/docs/18/sql-createtable.html\" title=\"CREATE TABLE\" rel=\"nofollow\"\u003e\u003cspan\u003eCREATE TABLE\u003c/span\u003e\u003c/a\u003e。它们只能为约束触发器指定。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eREFERENCING\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e这个关键字紧接在一个或两个关系名的声明之前，这些关系名可用于访问触发语句的过渡关系。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eOLD TABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ccode\u003eNEW TABLE\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e该子句指示后面的关系名是用于前映像过渡关系还是后映像过渡关系。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003etransition_relation_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e该过渡关系在触发器内部使用的名称（非限定名）。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eFOR EACH ROW\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ccode\u003eFOR EACH STATEMENT\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e这指定触发器函数是对受触发事件影响的每一行执行一次，还是每条 SQL 语句仅执行一次。如果两者都未指定，默认值是\u003ccode\u003eFOR EACH STATEMENT\u003c/code\u003e。约束触发器只能指定为\u003ccode\u003eFOR EACH ROW\u003c/code\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003econdition\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e一个布尔表达式，用于决定触发器函数是否会实际执行。如果指定了\u003ccode\u003eWHEN\u003c/code\u003e，则仅当 \u003cem\u003e\u003ccode\u003econdition\u003c/code\u003e\u003c/em\u003e 返回 \u003ccode\u003etrue\u003c/code\u003e 时才会调用该函数。在 \u003ccode\u003eFOR EACH ROW\u003c/code\u003e 触发器中，\u003ccode\u003eWHEN\u003c/code\u003e 条件可以分别写成 \u003ccode\u003eOLD.\u003cem\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e 或 \u003ccode\u003eNEW.\u003cem\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e 来引用旧行值和/或新行值中的列。当然，\u003ccode\u003eINSERT\u003c/code\u003e 触发器不能引用 \u003ccode\u003eOLD\u003c/code\u003e，而 \u003ccode\u003eDELETE\u003c/code\u003e 触发器不能引用 \u003ccode\u003eNEW\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e\u003ccode\u003eINSTEAD OF\u003c/code\u003e触发器不支持\u003ccode\u003eWHEN\u003c/code\u003e条件。\u003c/p\u003e\u003cp\u003e当前，\u003ccode\u003eWHEN\u003c/code\u003e 表达式不能包含子查询。\u003c/p\u003e\u003cp\u003e请注意，对于约束触发器，\u003ccode\u003eWHEN\u003c/code\u003e 条件的求值不会被延迟，而是在行操作执行后立即发生。如果该条件求值结果不为真，则该触发器不会被加入延迟执行队列。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003efunction_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e一个由用户提供的函数，声明为不接受参数并返回 \u003ccode\u003etrigger\u003c/code\u003e 类型；当触发器被触发时，就会执行该函数。\u003c/p\u003e\u003cp\u003e在\u003ccode\u003eCREATE TRIGGER\u003c/code\u003e的语法中，关键字 \u003ccode\u003eFUNCTION\u003c/code\u003e 和 \u003ccode\u003ePROCEDURE\u003c/code\u003e 是等价的，但无论如何，被引用的对象都必须是函数而不是过程。这里使用 \u003ccode\u003ePROCEDURE\u003c/code\u003e 关键字只是出于历史原因，并且已被弃用。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003earguments\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e一个可选的、以逗号分隔的参数列表，在执行触发器时会传给该函数。这些参数是字符串字面常量。这里也可以写简单名称和数字常量，但它们都会被转换成字符串。请查阅该触发器函数所用实现语言的说明，以了解如何在函数内部访问这些参数；这可能与普通函数参数不同。\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e注解\u003c/h2\u003e\u003cp\u003e要在表上创建或替换触发器，用户必须拥有该表上的 \u003ccode\u003eTRIGGER\u003c/code\u003e 权限。用户还必须拥有该触发器函数上的 \u003ccode\u003eEXECUTE\u003c/code\u003e 权限。\u003c/p\u003e\u003cp\u003e使用\u003ca href=\"/docs/18/sql-droptrigger.html\" title=\"DROP TRIGGER\" rel=\"nofollow\"\u003e\u003ccode\u003eDROP TRIGGER\u003c/code\u003e\u003c/a\u003e移除触发器。\u003c/p\u003e\u003cp\u003e在分区表上创建行级触发器，会在它的每个现有分区上创建一个相同的\u003cspan\u003e“\u003cspan\u003e克隆\u003c/span\u003e”\u003c/span\u003e触发器；以后创建或附加的任何分区也都会拥有相同的触发器。如果某个子分区上已经存在同名且相冲突的触发器，则会报错，除非使用 \u003ccode\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e，在这种情况下，该触发器会被克隆触发器替换。当某个分区从其父表分离时，它的克隆触发器也会被移除。\u003c/p\u003e\u003cp\u003e列限定触发器（即使用 \u003ccode\u003eUPDATE OF \u003cem\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e 语法定义的触发器）会在其任一列被列为 \u003ccode\u003eUPDATE\u003c/code\u003e 命令的 \u003ccode\u003eSET\u003c/code\u003e 列表目标时引发。即便触发器没有引发，列值仍有可能发生变化，因为 \u003ccode\u003eBEFORE UPDATE\u003c/code\u003e 触发器对行内容所作的更改不会被考虑在内。反过来，诸如 \u003ccode\u003eUPDATE ... SET x = x ...\u003c/code\u003e 这样的命令会引发列 \u003ccode\u003ex\u003c/code\u003e 上的触发器，即使该列的值实际上并未变化。\u003c/p\u003e\u003cp\u003e在 \u003ccode\u003eBEFORE\u003c/code\u003e 触发器中，\u003ccode\u003eWHEN\u003c/code\u003e 条件会在函数执行前（或者本应执行前）立即求值，因此使用 \u003ccode\u003eWHEN\u003c/code\u003e 与在触发器函数开头测试相同条件并无实质区别。特别要注意的是，该条件看到的 \u003ccode\u003eNEW\u003c/code\u003e 行是当前值，它可能已经被更早触发的触发器修改过。此外，\u003ccode\u003eBEFORE\u003c/code\u003e 触发器的 \u003ccode\u003eWHEN\u003c/code\u003e 条件不允许检查 \u003ccode\u003eNEW\u003c/code\u003e 行的系统列（例如 \u003ccode\u003ectid\u003c/code\u003e），因为这些列此时尚未被设置。\u003c/p\u003e\u003cp\u003e在 \u003ccode\u003eAFTER\u003c/code\u003e 触发器中，\u003ccode\u003eWHEN\u003c/code\u003e 条件会在行变更发生后立即求值，并决定是否将一个事件排入队列，以便在语句结束时引发该触发器。因此，当 \u003ccode\u003eAFTER\u003c/code\u003e 触发器的 \u003ccode\u003eWHEN\u003c/code\u003e 条件不返回真时，就没有必要将事件排队，也不需要在语句结束时重新获取该行。如果触发器只需针对少数几行引发，这能显著提升那些会修改很多行的语句的速度。\u003c/p\u003e\u003cp\u003e在某些情况下，一条 SQL 命令可能会引发不止一种触发器。例如，带有 \u003ccode\u003eON CONFLICT DO UPDATE\u003c/code\u003e 子句的 \u003ccode\u003eINSERT\u003c/code\u003e 可能同时导致插入和更新操作，因此会按需引发这两类触发器。提供给触发器的过渡关系是按事件类型区分的；因此 \u003ccode\u003eINSERT\u003c/code\u003e 触发器只能看到插入的行，而 \u003ccode\u003eUPDATE\u003c/code\u003e 触发器只能看到更新的行。\u003c/p\u003e\u003cp\u003e由外键强制执行动作导致的行更新或删除，例如 \u003ccode\u003eON UPDATE CASCADE\u003c/code\u003e 或 \u003ccode\u003eON DELETE SET NULL\u003c/code\u003e，会被视为导致它们发生的那条 SQL 命令的一部分（注意，这类动作永远不会被延迟）。受影响表上的相关触发器也会被引发，因而这提供了另一种情形，使得一条 SQL 命令可能引发与其类型并不直接匹配的触发器。在简单情况下，请求过渡关系的触发器会把单条原始 SQL 命令在该表中造成的所有更改视为一个过渡关系。不过，在某些情况下，如果存在一个请求过渡关系的 \u003ccode\u003eAFTER ROW\u003c/code\u003e 触发器，就会使单条 SQL 命令触发的外键强制执行动作被拆分成多个步骤，每一步都有各自的过渡关系。在这种情况下，任何存在的语句级触发器都会在每组过渡关系创建时引发一次，从而保证这些触发器在过渡关系中看到每个受影响的行时，恰好只看到一次。\u003c/p\u003e\u003cp\u003e只有当视图上的动作由行级 \u003ccode\u003eINSTEAD OF\u003c/code\u003e 触发器处理时，视图上的语句级触发器才会引发。如果该动作由 \u003ccode\u003eINSTEAD\u003c/code\u003e 规则处理，那么规则发出的那些语句会代替原先引用该视图的语句执行，因此最终引发的是替代语句所引用表上的触发器。同样，如果该视图是自动可更新的，那么该动作会通过把语句自动重写为作用于视图基表的动作来处理，因而最终引发的是基表上的语句级触发器。\u003c/p\u003e\u003cp\u003e修改分区表或带有继承子表的表时，会引发附着在显式命名表上的语句级触发器，但不会引发其分区或子表上的语句级触发器。相反，行级触发器会在受影响的分区或子表中的行上引发，即使这些分区或子表在查询中没有被显式命名。如果某个语句级触发器通过 \u003ccode\u003eREFERENCING\u003c/code\u003e 子句定义了过渡关系，那么所有受影响分区或子表中的行的前映像和后映像都是可见的。对于继承子表，这些行映像只包含该触发器所附着表中存在的列。\u003c/p\u003e\u003cp\u003e当前，带过渡关系的行级触发器不能定义在分区或继承子表上。此外，分区表上的触发器不能是 \u003ccode\u003eINSTEAD OF\u003c/code\u003e 触发器。\u003c/p\u003e\u003cp\u003e当前，\u003ccode\u003eOR REPLACE\u003c/code\u003e 选项不支持约束触发器。\u003c/p\u003e\u003cp\u003e不建议在已经对触发器所属表执行过修改操作的事务中替换现有触发器。已经作出的触发器引发决定，或者其中的一部分，不会重新考虑，因此其效果可能出人意料。\u003c/p\u003e\u003cp\u003e有少量内置触发器函数可用于解决常见问题，而无需自己编写触发器代码；参见\u003ca href=\"/docs/18/functions-trigger.html\" rel=\"nofollow\"\u003e第 9.29 节\u003c/a\u003e。\u003c/p\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e示例\u003c/h2\u003e\u003cp\u003e每当表 \u003ccode\u003eaccounts\u003c/code\u003e 中的一行即将被更新时，执行函数 \u003ccode\u003echeck_account_update\u003c/code\u003e：\u003c/p\u003e\u003cpre\u003eCREATE TRIGGER check_update\n    BEFORE UPDATE ON accounts\n    FOR EACH ROW\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003e修改该触发器定义，使其仅在 \u003ccode\u003eUPDATE\u003c/code\u003e 命令中将列 \u003ccode\u003ebalance\u003c/code\u003e 指定为目标时才执行该函数：\u003c/p\u003e\u003cpre\u003eCREATE OR REPLACE TRIGGER check_update\n    BEFORE UPDATE OF balance ON accounts\n    FOR EACH ROW\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003e这种写法只有在列 \u003ccode\u003ebalance\u003c/code\u003e 的值确实发生变化时才执行该函数：\u003c/p\u003e\u003cpre\u003eCREATE TRIGGER check_update\n    BEFORE UPDATE ON accounts\n    FOR EACH ROW\n    WHEN (OLD.balance IS DISTINCT FROM NEW.balance)\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003e调用一个函数记录 \u003ccode\u003eaccounts\u003c/code\u003e 的更新，但仅在确有内容发生变化时才调用：\u003c/p\u003e\u003cpre\u003eCREATE TRIGGER log_update\n    AFTER UPDATE ON accounts\n    FOR EACH ROW\n    WHEN (OLD.* IS DISTINCT FROM NEW.*)\n    EXECUTE FUNCTION log_account_update();\n\u003c/pre\u003e\u003cp\u003e针对每一行执行函数 \u003ccode\u003eview_insert_row\u003c/code\u003e，将行插入视图底层的表中：\u003c/p\u003e\u003cpre\u003eCREATE TRIGGER view_insert\n    INSTEAD OF INSERT ON my_view\n    FOR EACH ROW\n    EXECUTE FUNCTION view_insert_row();\n\u003c/pre\u003e\u003cp\u003e针对每条语句执行函数 \u003ccode\u003echeck_transfer_balances_to_zero\u003c/code\u003e，以确认 \u003ccode\u003etransfer\u003c/code\u003e 中这些行相互抵销后的净额为零：\u003c/p\u003e\u003cpre\u003eCREATE TRIGGER transfer_insert\n    AFTER INSERT ON transfer\n    REFERENCING NEW TABLE AS inserted\n    FOR EACH STATEMENT\n    EXECUTE FUNCTION check_transfer_balances_to_zero();\n\u003c/pre\u003e\u003cp\u003e针对每一行执行函数 \u003ccode\u003echeck_matching_pairs\u003c/code\u003e，以确认对配对项的更改会同时发生（即由同一条语句完成）：\u003c/p\u003e\u003cpre\u003eCREATE TRIGGER paired_items_update\n    AFTER UPDATE ON paired_items\n    REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab\n    FOR EACH ROW\n    EXECUTE FUNCTION check_matching_pairs();\n\u003c/pre\u003e\u003cp\u003e\u003ca href=\"/docs/18/trigger-example.html\" rel=\"nofollow\"\u003e第 37.4 节\u003c/a\u003e包含一个用 C 编写的触发器函数的完整示例。\u003c/p\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e兼容性\u003c/h2\u003e\u003cp\u003e\u003cspan\u003ePostgreSQL\u003c/span\u003e 中的 \u003ccode\u003eCREATE TRIGGER\u003c/code\u003e 语句实现了 \u003cacronym\u003eSQL\u003c/acronym\u003e 标准的一个子集。当前仍缺少以下功能：\u003c/p\u003e\u003cdiv\u003e\u003cul\u003e\u003cli\u003e\u003cp\u003e虽然 \u003ccode\u003eAFTER\u003c/code\u003e 触发器的过渡表名可以按标准方式用 \u003ccode\u003eREFERENCING\u003c/code\u003e 子句指定，但 \u003ccode\u003eFOR EACH ROW\u003c/code\u003e 触发器中使用的行变量不能在 \u003ccode\u003eREFERENCING\u003c/code\u003e 子句中指定。它们以依赖于触发器函数所用语言的方式提供，但对于任意一种语言而言，这种方式是固定的。有些语言的实际行为就像存在一个含有 \u003ccode\u003eOLD ROW AS OLD NEW ROW AS NEW\u003c/code\u003e 的 \u003ccode\u003eREFERENCING\u003c/code\u003e 子句一样。\u003c/p\u003e\u003c/li\u003e\u003cli\u003e\u003cp\u003e标准允许将过渡表与列限定 \u003ccode\u003eUPDATE\u003c/code\u003e 触发器一起使用，但这样一来，在过渡表中应当可见的行集合就取决于触发器的列列表。\u003cspan\u003ePostgreSQL\u003c/span\u003e 当前尚未实现这一点。\u003c/p\u003e\u003c/li\u003e\u003cli\u003e\u003cp\u003e\u003cspan\u003ePostgreSQL\u003c/span\u003e 只允许通过执行用户定义函数来完成触发动作。标准则允许把许多其他 SQL 命令（例如 \u003ccode\u003eCREATE TABLE\u003c/code\u003e）作为触发动作执行。通过创建一个执行所需命令的用户定义函数，这一限制并不难绕过。\u003c/p\u003e\u003c/li\u003e\u003c/ul\u003e\u003c/div\u003e\u003cp\u003eSQL 规定多个触发器应按创建时间顺序引发。\u003cspan\u003ePostgreSQL\u003c/span\u003e 则按名称顺序引发，这被认为更方便。\u003c/p\u003e\u003cp\u003eSQL 规定，级联删除上的 \u003ccode\u003eBEFORE DELETE\u003c/code\u003e 触发器应在级联 \u003ccode\u003eDELETE\u003c/code\u003e 完成\u003cspan\u003e\u003cem\u003e之后\u003c/em\u003e\u003c/span\u003e引发。\u003cspan\u003ePostgreSQL\u003c/span\u003e 的行为则是 \u003ccode\u003eBEFORE DELETE\u003c/code\u003e 总会在删除动作之前引发，即使是级联删除也一样。这被认为更一致。如果 \u003ccode\u003eBEFORE\u003c/code\u003e 触发器在由引用动作引起的更新过程中修改行或阻止更新，也会出现非标准行为。这可能导致约束违反，或者使存储的数据不符合引用约束。\u003c/p\u003e\u003cp\u003e使用 \u003ccode\u003eOR\u003c/code\u003e 为单个触发器指定多个动作的能力，是 \u003cspan\u003ePostgreSQL\u003c/span\u003e 对 SQL 标准的扩展。\u003c/p\u003e\u003cp\u003e能够在 \u003ccode\u003eTRUNCATE\u003c/code\u003e 上引发触发器，是 \u003cspan\u003ePostgreSQL\u003c/span\u003e 对 SQL 标准的扩展；能够在视图上定义语句级触发器也是如此。\u003c/p\u003e\u003cp\u003e\u003ccode\u003eCREATE CONSTRAINT TRIGGER\u003c/code\u003e 是 \u003cspan\u003ePostgreSQL\u003c/span\u003e 对 \u003cacronym\u003eSQL\u003c/acronym\u003e 标准的扩展。\u003ccode\u003eOR REPLACE\u003c/code\u003e 选项也是如此。\u003c/p\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e另见\u003c/h2\u003e\u003cspan\u003e\u003ca href=\"/wiki/sql/alter-trigger/?v=18\" title=\"ALTER TRIGGER\" rel=\"nofollow\"\u003e\u003cspan\u003eALTER TRIGGER\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/drop-trigger/?v=18\" title=\"DROP TRIGGER\" rel=\"nofollow\"\u003e\u003cspan\u003eDROP TRIGGER\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/create-function/?v=18\" title=\"CREATE FUNCTION\" rel=\"nofollow\"\u003e\u003cspan\u003eCREATE FUNCTION\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/set-constraints/?v=18\" title=\"SET CONSTRAINTS\" rel=\"nofollow\"\u003e\u003cspan\u003eSET CONSTRAINTS\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e\u003c/section\u003e","SourceRevision":"1b5ca64c","ContentHash":"ac3c7ff87bdf8ac41910de39789798185da4d88ac36b8e359094770d7a1df3b8","Payload":{"purpose_zh":"定义一个新触发器","sections":[{"html":"\u003cp\u003e\u003ccode class=\"command\"\u003eCREATE TRIGGER\u003c/code\u003e创建一个新触发器。\u003ccode class=\"command\"\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e要么创建一个新触发器，要么替换一个现有触发器。该触发器会关联到指定的表、视图或外部表，并在对该对象执行某些操作时执行指定的函数 \u003cem class=\"replaceable\"\u003e\u003ccode\u003efunction_name\u003c/code\u003e\u003c/em\u003e。\u003c/p\u003e\u003cp\u003e要替换现有触发器的当前定义，可使用 \u003ccode class=\"command\"\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e，并指定现有触发器的名称及其所属表。其他所有属性都会被替换。\u003c/p\u003e\u003cp\u003e可以指定触发器在尝试对某一行执行该操作之前引发（即在检查约束以及尝试执行\u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e、\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e或\u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e之前）；也可以在该操作完成之后引发（即在检查约束以及完成 \u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e、\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e或 \u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e之后）；或者取代该操作执行（用于视图上的插入、更新或删除）。如果触发器在事件之前引发，或者改为取代该事件执行，则它可以跳过对当前行的操作，或者修改待插入的行（仅适用于\u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e和\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e操作）。如果触发器在事件之后引发，则所有更改（包括其他触发器的影响）对该触发器都是\u003cspan class=\"quote\"\u003e“\u003cspan class=\"quote\"\u003e可见\u003c/span\u003e”\u003c/span\u003e的。\u003c/p\u003e\u003cp\u003e被标记为\u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e的触发器会针对该操作修改的每一行调用一次。例如，一个影响 10 行的\u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e会导致目标关系上的 \u003ccode class=\"literal\"\u003eON DELETE\u003c/code\u003e触发器分别被调用 10 次，即每个被删除的行调用一次。相比之下，被标记为\u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e的触发器对于任意给定操作只执行一次，不管该操作修改了多少行（特别是，修改零行的操作仍会导致所有适用的 \u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e触发器执行）。\u003c/p\u003e\u003cp\u003e被指定为取代该触发事件执行的 \u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e 触发器必须标记为 \u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e，并且只能定义在视图上。视图上的 \u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e 和 \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e 触发器必须标记为 \u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e此外，也可以把触发器定义为在 \u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e 时引发，但只能是 \u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e下表汇总了哪些类型的触发器可用于表、视图和外部表：\u003c/p\u003e\u003cdiv class=\"informaltable\"\u003e\u003ctable class=\"informaltable\"\u003e\u003cthead\u003e\u003ctr\u003e\u003cth\u003e时机\u003c/th\u003e\u003cth\u003e事件\u003c/th\u003e\u003cth\u003e行级\u003c/th\u003e\u003cth\u003e语句级\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\u003ctbody\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e/\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e/\u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e表和外部表\u003c/td\u003e\u003ctd\u003e表、视图和外部表\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003e表和外部表\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e/\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e/\u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e表和外部表\u003c/td\u003e\u003ctd\u003e表、视图和外部表\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003e表和外部表\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e\u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e/\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e/\u003ccode class=\"command\"\u003eDELETE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e视图\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003ctd\u003e—\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e\u003c/div\u003e\u003cp\u003e此外，触发器定义还可以指定一个布尔型 \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件，用于测试是否应当引发该触发器。在行级触发器中，\u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件可以检查该行各列的新值和/或旧值。语句级触发器也可以带有 \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件，不过这一特性对它们的用处不大，因为该条件无法引用表中的任何值。\u003c/p\u003e\u003cp\u003e如果针对同一事件定义了多个同类触发器，它们将按名称的字母顺序引发。\u003c/p\u003e\u003cp\u003e当指定\u003ccode class=\"literal\"\u003eCONSTRAINT\u003c/code\u003e选项时，此命令会创建一个\u003cem class=\"firstterm\"\u003e约束触发器\u003c/em\u003e。这与普通触发器相同，只是可以使用 \u003ca href=\"/docs/18/sql-set-constraints.html\" title=\"SET CONSTRAINTS\"\u003e\u003ccode class=\"command\"\u003eSET CONSTRAINTS\u003c/code\u003e\u003c/a\u003e 调整其引发时机。约束触发器必须是普通表（不是外部表）上的 \u003ccode class=\"literal\"\u003eAFTER ROW\u003c/code\u003e 触发器。它们既可以在导致触发事件的语句结束时引发，也可以在包含它的事务结束时引发；后一种情况称为\u003cem class=\"firstterm\"\u003e延迟\u003c/em\u003e。也可以使用\u003ccode class=\"command\"\u003eSET CONSTRAINTS\u003c/code\u003e强制尚待执行的延迟触发器立即引发。约束触发器应当在其所实现的约束遭到违反时抛出异常。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e选项允许收集\u003cem class=\"firstterm\"\u003e过渡关系\u003c/em\u003e，即包含当前 SQL 语句插入、删除或修改的全部行的行集合。该特性使触发器能够看到该语句所做更改的整体视图，而不是一次只看到一行。该选项仅允许用于普通表（不是外部表）上的 \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e 触发器。该触发器还不能是约束触发器。此外，如果触发器是 \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e 触发器，则在使用该选项时不得指定 \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e 列表。\u003ccode class=\"literal\"\u003eOLD TABLE\u003c/code\u003e 最多只能指定一次，并且只能用于可能在 \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003eDELETE\u003c/code\u003e 上引发的触发器；它会创建一个过渡关系，其中包含该语句更新或删除的全部行的\u003cem class=\"firstterm\"\u003e前映像\u003c/em\u003e。同样，\u003ccode class=\"literal\"\u003eNEW TABLE\u003c/code\u003e 最多只能指定一次，并且只能用于可能在 \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003eINSERT\u003c/code\u003e 上引发的触发器；它会创建一个过渡关系，其中包含该语句更新或插入的全部行的\u003cem class=\"firstterm\"\u003e后映像\u003c/em\u003e。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"command\"\u003eSELECT\u003c/code\u003e 不会修改任何行，因此无法创建 \u003ccode class=\"command\"\u003eSELECT\u003c/code\u003e 触发器。对于那些看起来需要 \u003ccode class=\"command\"\u003eSELECT\u003c/code\u003e 触发器的问题，规则和视图可能提供可行的解决方案。\u003c/p\u003e\u003cp\u003e参见\u003ca href=\"/docs/18/triggers.html\" title=\"第 37 章 触发器\"\u003e第 37 章\u003c/a\u003e以了解有关触发器的更多信息。\u003c/p\u003e","key":"description","title":"描述"},{"html":"\u003cdiv class=\"variablelist\"\u003e\u003cdl class=\"variablelist\"\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e新触发器的名称。它必须不同于同一表上的任何其他触发器名称。该名称不能是模式限定名 — 触发器会继承其所属表的模式。对于约束触发器，这也是使用\u003ccode class=\"command\"\u003eSET CONSTRAINTS\u003c/code\u003e 修改触发器行为时所使用的名称。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e决定该函数是在事件之前、之后调用，还是取代该事件执行。约束触发器只能指定为\u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003eevent\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003eINSERT\u003c/code\u003e、\u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e、\u003ccode class=\"literal\"\u003eDELETE\u003c/code\u003e或 \u003ccode class=\"literal\"\u003eTRUNCATE\u003c/code\u003e 之一；它指定了哪个事件将引发该触发器。除请求过渡关系的情况外，可以使用\u003ccode class=\"literal\"\u003eOR\u003c/code\u003e指定多个事件。\u003c/p\u003e\u003cp\u003e对于\u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e事件，可以使用以下语法指定列列表：\u003c/p\u003e\u003cpre class=\"synopsis\"\u003eUPDATE OF \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name1\u003c/code\u003e\u003c/em\u003e [, \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name2\u003c/code\u003e\u003c/em\u003e ... ]\n\u003c/pre\u003e\u003cp\u003e只有当列出的列中至少有一列被列为\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e命令的目标列时，或者列出的列中有某个生成列依赖于作为\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e目标的列时，该触发器才会引发。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003eINSTEAD OF UPDATE\u003c/code\u003e 事件不允许指定列列表。请求过渡关系时也不能指定列列表。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003etable_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e该触发器所作用的表、视图或外部表的名称（可以是模式限定的）。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003ereferenced_table_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e约束所引用的另一张表的名称（可以是模式限定的）。此选项用于外键约束，不建议用于一般用途。它只能为约束触发器指定。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eDEFERRABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eNOT DEFERRABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eINITIALLY IMMEDIATE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eINITIALLY DEFERRED\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e触发器的默认时机。有关这些约束选项的细节，请参见\u003ca href=\"/docs/18/sql-createtable.html\" title=\"CREATE TABLE\"\u003e\u003cspan class=\"refentrytitle\"\u003eCREATE TABLE\u003c/span\u003e\u003c/a\u003e。它们只能为约束触发器指定。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e这个关键字紧接在一个或两个关系名的声明之前，这些关系名可用于访问触发语句的过渡关系。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eOLD TABLE\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eNEW TABLE\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e该子句指示后面的关系名是用于前映像过渡关系还是后映像过渡关系。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003etransition_relation_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e该过渡关系在触发器内部使用的名称（非限定名）。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e这指定触发器函数是对受触发事件影响的每一行执行一次，还是每条 SQL 语句仅执行一次。如果两者都未指定，默认值是\u003ccode class=\"literal\"\u003eFOR EACH STATEMENT\u003c/code\u003e。约束触发器只能指定为\u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003econdition\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e一个布尔表达式，用于决定触发器函数是否会实际执行。如果指定了\u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e，则仅当 \u003cem class=\"replaceable\"\u003e\u003ccode\u003econdition\u003c/code\u003e\u003c/em\u003e 返回 \u003ccode class=\"literal\"\u003etrue\u003c/code\u003e 时才会调用该函数。在 \u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e 触发器中，\u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件可以分别写成 \u003ccode class=\"literal\"\u003eOLD.\u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003eNEW.\u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e 来引用旧行值和/或新行值中的列。当然，\u003ccode class=\"literal\"\u003eINSERT\u003c/code\u003e 触发器不能引用 \u003ccode class=\"literal\"\u003eOLD\u003c/code\u003e，而 \u003ccode class=\"literal\"\u003eDELETE\u003c/code\u003e 触发器不能引用 \u003ccode class=\"literal\"\u003eNEW\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e触发器不支持\u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e条件。\u003c/p\u003e\u003cp\u003e当前，\u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 表达式不能包含子查询。\u003c/p\u003e\u003cp\u003e请注意，对于约束触发器，\u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件的求值不会被延迟，而是在行操作执行后立即发生。如果该条件求值结果不为真，则该触发器不会被加入延迟执行队列。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003efunction_name\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e一个由用户提供的函数，声明为不接受参数并返回 \u003ccode class=\"literal\"\u003etrigger\u003c/code\u003e 类型；当触发器被触发时，就会执行该函数。\u003c/p\u003e\u003cp\u003e在\u003ccode class=\"literal\"\u003eCREATE TRIGGER\u003c/code\u003e的语法中，关键字 \u003ccode class=\"literal\"\u003eFUNCTION\u003c/code\u003e 和 \u003ccode class=\"literal\"\u003ePROCEDURE\u003c/code\u003e 是等价的，但无论如何，被引用的对象都必须是函数而不是过程。这里使用 \u003ccode class=\"literal\"\u003ePROCEDURE\u003c/code\u003e 关键字只是出于历史原因，并且已被弃用。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003earguments\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e一个可选的、以逗号分隔的参数列表，在执行触发器时会传给该函数。这些参数是字符串字面常量。这里也可以写简单名称和数字常量，但它们都会被转换成字符串。请查阅该触发器函数所用实现语言的说明，以了解如何在函数内部访问这些参数；这可能与普通函数参数不同。\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e","key":"parameters","title":"参数"},{"html":"\u003cp\u003e要在表上创建或替换触发器，用户必须拥有该表上的 \u003ccode class=\"literal\"\u003eTRIGGER\u003c/code\u003e 权限。用户还必须拥有该触发器函数上的 \u003ccode class=\"literal\"\u003eEXECUTE\u003c/code\u003e 权限。\u003c/p\u003e\u003cp\u003e使用\u003ca href=\"/docs/18/sql-droptrigger.html\" title=\"DROP TRIGGER\"\u003e\u003ccode class=\"command\"\u003eDROP TRIGGER\u003c/code\u003e\u003c/a\u003e移除触发器。\u003c/p\u003e\u003cp\u003e在分区表上创建行级触发器，会在它的每个现有分区上创建一个相同的\u003cspan class=\"quote\"\u003e“\u003cspan class=\"quote\"\u003e克隆\u003c/span\u003e”\u003c/span\u003e触发器；以后创建或附加的任何分区也都会拥有相同的触发器。如果某个子分区上已经存在同名且相冲突的触发器，则会报错，除非使用 \u003ccode class=\"command\"\u003eCREATE OR REPLACE TRIGGER\u003c/code\u003e，在这种情况下，该触发器会被克隆触发器替换。当某个分区从其父表分离时，它的克隆触发器也会被移除。\u003c/p\u003e\u003cp\u003e列限定触发器（即使用 \u003ccode class=\"literal\"\u003eUPDATE OF \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e\u003c/code\u003e 语法定义的触发器）会在其任一列被列为 \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e 命令的 \u003ccode class=\"literal\"\u003eSET\u003c/code\u003e 列表目标时引发。即便触发器没有引发，列值仍有可能发生变化，因为 \u003ccode class=\"literal\"\u003eBEFORE UPDATE\u003c/code\u003e 触发器对行内容所作的更改不会被考虑在内。反过来，诸如 \u003ccode class=\"literal\"\u003eUPDATE ... SET x = x ...\u003c/code\u003e 这样的命令会引发列 \u003ccode class=\"literal\"\u003ex\u003c/code\u003e 上的触发器，即使该列的值实际上并未变化。\u003c/p\u003e\u003cp\u003e在 \u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e 触发器中，\u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件会在函数执行前（或者本应执行前）立即求值，因此使用 \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 与在触发器函数开头测试相同条件并无实质区别。特别要注意的是，该条件看到的 \u003ccode class=\"literal\"\u003eNEW\u003c/code\u003e 行是当前值，它可能已经被更早触发的触发器修改过。此外，\u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e 触发器的 \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件不允许检查 \u003ccode class=\"literal\"\u003eNEW\u003c/code\u003e 行的系统列（例如 \u003ccode class=\"literal\"\u003ectid\u003c/code\u003e），因为这些列此时尚未被设置。\u003c/p\u003e\u003cp\u003e在 \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e 触发器中，\u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件会在行变更发生后立即求值，并决定是否将一个事件排入队列，以便在语句结束时引发该触发器。因此，当 \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e 触发器的 \u003ccode class=\"literal\"\u003eWHEN\u003c/code\u003e 条件不返回真时，就没有必要将事件排队，也不需要在语句结束时重新获取该行。如果触发器只需针对少数几行引发，这能显著提升那些会修改很多行的语句的速度。\u003c/p\u003e\u003cp\u003e在某些情况下，一条 SQL 命令可能会引发不止一种触发器。例如，带有 \u003ccode class=\"literal\"\u003eON CONFLICT DO UPDATE\u003c/code\u003e 子句的 \u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e 可能同时导致插入和更新操作，因此会按需引发这两类触发器。提供给触发器的过渡关系是按事件类型区分的；因此 \u003ccode class=\"command\"\u003eINSERT\u003c/code\u003e 触发器只能看到插入的行，而 \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e 触发器只能看到更新的行。\u003c/p\u003e\u003cp\u003e由外键强制执行动作导致的行更新或删除，例如 \u003ccode class=\"literal\"\u003eON UPDATE CASCADE\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003eON DELETE SET NULL\u003c/code\u003e，会被视为导致它们发生的那条 SQL 命令的一部分（注意，这类动作永远不会被延迟）。受影响表上的相关触发器也会被引发，因而这提供了另一种情形，使得一条 SQL 命令可能引发与其类型并不直接匹配的触发器。在简单情况下，请求过渡关系的触发器会把单条原始 SQL 命令在该表中造成的所有更改视为一个过渡关系。不过，在某些情况下，如果存在一个请求过渡关系的 \u003ccode class=\"literal\"\u003eAFTER ROW\u003c/code\u003e 触发器，就会使单条 SQL 命令触发的外键强制执行动作被拆分成多个步骤，每一步都有各自的过渡关系。在这种情况下，任何存在的语句级触发器都会在每组过渡关系创建时引发一次，从而保证这些触发器在过渡关系中看到每个受影响的行时，恰好只看到一次。\u003c/p\u003e\u003cp\u003e只有当视图上的动作由行级 \u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e 触发器处理时，视图上的语句级触发器才会引发。如果该动作由 \u003ccode class=\"literal\"\u003eINSTEAD\u003c/code\u003e 规则处理，那么规则发出的那些语句会代替原先引用该视图的语句执行，因此最终引发的是替代语句所引用表上的触发器。同样，如果该视图是自动可更新的，那么该动作会通过把语句自动重写为作用于视图基表的动作来处理，因而最终引发的是基表上的语句级触发器。\u003c/p\u003e\u003cp\u003e修改分区表或带有继承子表的表时，会引发附着在显式命名表上的语句级触发器，但不会引发其分区或子表上的语句级触发器。相反，行级触发器会在受影响的分区或子表中的行上引发，即使这些分区或子表在查询中没有被显式命名。如果某个语句级触发器通过 \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e 子句定义了过渡关系，那么所有受影响分区或子表中的行的前映像和后映像都是可见的。对于继承子表，这些行映像只包含该触发器所附着表中存在的列。\u003c/p\u003e\u003cp\u003e当前，带过渡关系的行级触发器不能定义在分区或继承子表上。此外，分区表上的触发器不能是 \u003ccode class=\"literal\"\u003eINSTEAD OF\u003c/code\u003e 触发器。\u003c/p\u003e\u003cp\u003e当前，\u003ccode class=\"literal\"\u003eOR REPLACE\u003c/code\u003e 选项不支持约束触发器。\u003c/p\u003e\u003cp\u003e不建议在已经对触发器所属表执行过修改操作的事务中替换现有触发器。已经作出的触发器引发决定，或者其中的一部分，不会重新考虑，因此其效果可能出人意料。\u003c/p\u003e\u003cp\u003e有少量内置触发器函数可用于解决常见问题，而无需自己编写触发器代码；参见\u003ca href=\"/docs/18/functions-trigger.html\" title=\"9.29. 触发器函数\"\u003e第 9.29 节\u003c/a\u003e。\u003c/p\u003e","key":"notes","title":"注解"},{"html":"\u003cp\u003e每当表 \u003ccode class=\"literal\"\u003eaccounts\u003c/code\u003e 中的一行即将被更新时，执行函数 \u003ccode class=\"function\"\u003echeck_account_update\u003c/code\u003e：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER check_update\n    BEFORE UPDATE ON accounts\n    FOR EACH ROW\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003e修改该触发器定义，使其仅在 \u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e 命令中将列 \u003ccode class=\"literal\"\u003ebalance\u003c/code\u003e 指定为目标时才执行该函数：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE OR REPLACE TRIGGER check_update\n    BEFORE UPDATE OF balance ON accounts\n    FOR EACH ROW\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003e这种写法只有在列 \u003ccode class=\"literal\"\u003ebalance\u003c/code\u003e 的值确实发生变化时才执行该函数：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER check_update\n    BEFORE UPDATE ON accounts\n    FOR EACH ROW\n    WHEN (OLD.balance IS DISTINCT FROM NEW.balance)\n    EXECUTE FUNCTION check_account_update();\n\u003c/pre\u003e\u003cp\u003e调用一个函数记录 \u003ccode class=\"literal\"\u003eaccounts\u003c/code\u003e 的更新，但仅在确有内容发生变化时才调用：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER log_update\n    AFTER UPDATE ON accounts\n    FOR EACH ROW\n    WHEN (OLD.* IS DISTINCT FROM NEW.*)\n    EXECUTE FUNCTION log_account_update();\n\u003c/pre\u003e\u003cp\u003e针对每一行执行函数 \u003ccode class=\"function\"\u003eview_insert_row\u003c/code\u003e，将行插入视图底层的表中：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER view_insert\n    INSTEAD OF INSERT ON my_view\n    FOR EACH ROW\n    EXECUTE FUNCTION view_insert_row();\n\u003c/pre\u003e\u003cp\u003e针对每条语句执行函数 \u003ccode class=\"function\"\u003echeck_transfer_balances_to_zero\u003c/code\u003e，以确认 \u003ccode class=\"literal\"\u003etransfer\u003c/code\u003e 中这些行相互抵销后的净额为零：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER transfer_insert\n    AFTER INSERT ON transfer\n    REFERENCING NEW TABLE AS inserted\n    FOR EACH STATEMENT\n    EXECUTE FUNCTION check_transfer_balances_to_zero();\n\u003c/pre\u003e\u003cp\u003e针对每一行执行函数 \u003ccode class=\"function\"\u003echeck_matching_pairs\u003c/code\u003e，以确认对配对项的更改会同时发生（即由同一条语句完成）：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003eCREATE TRIGGER paired_items_update\n    AFTER UPDATE ON paired_items\n    REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab\n    FOR EACH ROW\n    EXECUTE FUNCTION check_matching_pairs();\n\u003c/pre\u003e\u003cp\u003e\u003ca href=\"/docs/18/trigger-example.html\" title=\"37.4. 一个完整的触发器示例\"\u003e第 37.4 节\u003c/a\u003e包含一个用 C 编写的触发器函数的完整示例。\u003c/p\u003e","key":"examples","title":"示例"},{"html":"\u003cp\u003e\u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 中的 \u003ccode class=\"command\"\u003eCREATE TRIGGER\u003c/code\u003e 语句实现了 \u003cacronym\u003eSQL\u003c/acronym\u003e 标准的一个子集。当前仍缺少以下功能：\u003c/p\u003e\u003cdiv class=\"itemizedlist\"\u003e\u003cul class=\"itemizedlist\"\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e虽然 \u003ccode class=\"literal\"\u003eAFTER\u003c/code\u003e 触发器的过渡表名可以按标准方式用 \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e 子句指定，但 \u003ccode class=\"literal\"\u003eFOR EACH ROW\u003c/code\u003e 触发器中使用的行变量不能在 \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e 子句中指定。它们以依赖于触发器函数所用语言的方式提供，但对于任意一种语言而言，这种方式是固定的。有些语言的实际行为就像存在一个含有 \u003ccode class=\"literal\"\u003eOLD ROW AS OLD NEW ROW AS NEW\u003c/code\u003e 的 \u003ccode class=\"literal\"\u003eREFERENCING\u003c/code\u003e 子句一样。\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e标准允许将过渡表与列限定 \u003ccode class=\"literal\"\u003eUPDATE\u003c/code\u003e 触发器一起使用，但这样一来，在过渡表中应当可见的行集合就取决于触发器的列列表。\u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 当前尚未实现这一点。\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 只允许通过执行用户定义函数来完成触发动作。标准则允许把许多其他 SQL 命令（例如 \u003ccode class=\"command\"\u003eCREATE TABLE\u003c/code\u003e）作为触发动作执行。通过创建一个执行所需命令的用户定义函数，这一限制并不难绕过。\u003c/p\u003e\u003c/li\u003e\u003c/ul\u003e\u003c/div\u003e\u003cp\u003eSQL 规定多个触发器应按创建时间顺序引发。\u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 则按名称顺序引发，这被认为更方便。\u003c/p\u003e\u003cp\u003eSQL 规定，级联删除上的 \u003ccode class=\"literal\"\u003eBEFORE DELETE\u003c/code\u003e 触发器应在级联 \u003ccode class=\"literal\"\u003eDELETE\u003c/code\u003e 完成\u003cspan class=\"emphasis\"\u003e\u003cem\u003e之后\u003c/em\u003e\u003c/span\u003e引发。\u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 的行为则是 \u003ccode class=\"literal\"\u003eBEFORE DELETE\u003c/code\u003e 总会在删除动作之前引发，即使是级联删除也一样。这被认为更一致。如果 \u003ccode class=\"literal\"\u003eBEFORE\u003c/code\u003e 触发器在由引用动作引起的更新过程中修改行或阻止更新，也会出现非标准行为。这可能导致约束违反，或者使存储的数据不符合引用约束。\u003c/p\u003e\u003cp\u003e使用 \u003ccode class=\"literal\"\u003eOR\u003c/code\u003e 为单个触发器指定多个动作的能力，是 \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 对 SQL 标准的扩展。\u003c/p\u003e\u003cp\u003e能够在 \u003ccode class=\"command\"\u003eTRUNCATE\u003c/code\u003e 上引发触发器，是 \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 对 SQL 标准的扩展；能够在视图上定义语句级触发器也是如此。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"command\"\u003eCREATE CONSTRAINT TRIGGER\u003c/code\u003e 是 \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e 对 \u003cacronym\u003eSQL\u003c/acronym\u003e 标准的扩展。\u003ccode class=\"literal\"\u003eOR REPLACE\u003c/code\u003e 选项也是如此。\u003c/p\u003e","key":"compatibility","title":"兼容性"},{"html":"\u003cspan class=\"simplelist\"\u003e\u003ca href=\"/wiki/sql/alter-trigger/?v=18\" title=\"ALTER TRIGGER\"\u003e\u003cspan class=\"refentrytitle\"\u003eALTER TRIGGER\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/drop-trigger/?v=18\" title=\"DROP TRIGGER\"\u003e\u003cspan class=\"refentrytitle\"\u003eDROP TRIGGER\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/create-function/?v=18\" title=\"CREATE FUNCTION\"\u003e\u003cspan class=\"refentrytitle\"\u003eCREATE FUNCTION\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"/wiki/sql/set-constraints/?v=18\" title=\"SET CONSTRAINTS\"\u003e\u003cspan class=\"refentrytitle\"\u003eSET CONSTRAINTS\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e","key":"see_also","title":"另见"}],"sections_same_as":"","synopsis_html":"CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER \u003cem class=\"replaceable\"\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/em\u003e { BEFORE | AFTER | INSTEAD OF } { \u003cem class=\"replaceable\"\u003e\u003ccode\u003eevent\u003c/code\u003e\u003c/em\u003e [ OR ... ] }\n    ON \u003cem class=\"replaceable\"\u003e\u003ccode\u003etable_name\u003c/code\u003e\u003c/em\u003e\n    [ FROM \u003cem class=\"replaceable\"\u003e\u003ccode\u003ereferenced_table_name\u003c/code\u003e\u003c/em\u003e ]\n    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]\n    [ REFERENCING { { OLD | NEW } TABLE [ AS ] \u003cem class=\"replaceable\"\u003e\u003ccode\u003etransition_relation_name\u003c/code\u003e\u003c/em\u003e } [ ... ] ]\n    [ FOR [ EACH ] { ROW | STATEMENT } ]\n    [ WHEN ( \u003cem class=\"replaceable\"\u003e\u003ccode\u003econdition\u003c/code\u003e\u003c/em\u003e ) ]\n    EXECUTE { FUNCTION | PROCEDURE } \u003cem class=\"replaceable\"\u003e\u003ccode\u003efunction_name\u003c/code\u003e\u003c/em\u003e ( \u003cem class=\"replaceable\"\u003e\u003ccode\u003earguments\u003c/code\u003e\u003c/em\u003e )\n\n\u003cspan class=\"phrase\"\u003e其中 \u003cem class=\"replaceable\"\u003e\u003ccode\u003eevent\u003c/code\u003e\u003c/em\u003e 可以是以下之一：\u003c/span\u003e\n\n    INSERT\n    UPDATE [ OF \u003cem class=\"replaceable\"\u003e\u003ccode\u003ecolumn_name\u003c/code\u003e\u003c/em\u003e [, ... ] ]\n    DELETE\n    TRUNCATE","synopsis_text":"CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }\nON table_name\n[ FROM referenced_table_name ]\n[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]\n[ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]\n[ FOR [ EACH ] { ROW | STATEMENT } ]\n[ WHEN ( condition ) ]\nEXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )\n\n其中 event 可以是以下之一：\n\nINSERT\nUPDATE [ OF column_name [, ... ] ]\nDELETE\nTRUNCATE"}},"RequestedLocale":"zh-Hans","Fallback":false,"Versions":["10","11","12","13","14","15","16","17","18","19","20","6.4","6.5","7.0","7.1","7.2","7.3","7.4","8.0","8.1","8.2","8.3","8.4","9.0","9.1","9.2","9.3","9.4","9.5","9.6"],"Locales":["en","zh-Hans"],"Signatures":null,"Spellings":null,"SQLState":null,"Evidence":null}
