CREATE TRIGGER

CREATE TRIGGER

특정 테이블에서 데이터가 바뀔 때마다 자동으로 뭔가 실행되게 하고 싶을 때가 있어요. 예를 들어 행이 갱신되기 전에 값을 검증하거나, 삭제될 때 로그를 남기거나 말이죠. CREATE TRIGGER가 그런 트리거를 새로 만드는 명령이에요. 테이블·뷰·외부 테이블에 연결되어, 그 테이블에서 특정 작업이 일어날 때 지정한 함수 function_name을 실행해 줘요.

기존 트리거의 정의를 바꾸고 싶다면 CREATE OR REPLACE TRIGGER를 쓰면 돼요. 기존 트리거의 이름과 부모 테이블을 지정하면 나머지 속성은 모두 새 정의로 대체돼요.

트리거는 세 가지 시점에 발화할 수 있어요. 작업이 시도되기 전(제약 조건 확인과 INSERT·UPDATE·DELETE 시도 전), 작업이 완료된 후, 또는 뷰에 대한 삽입·갱신·삭제의 경우엔 작업 대신(instead of) 발화할 수 있어요. 트리거가 이벤트 전이나 대신 발화하면, 현재 행에 대한 작업을 건너뛰거나(INSERT·UPDATE 작업만) 삽입될 행을 바꿀 수 있어요. 이벤트 후에 발화하면 다른 트리거의 효과를 포함한 모든 변경이 트리거에 "보여요".

FOR EACH ROW로 표시된 트리거는 작업이 수정하는 각 행마다 한 번씩 호출돼요. 예를 들어 10개 행에 영향을 주는 DELETE는 대상 릴레이션의 ON DELETE 트리거를 삭제된 행마다 각각 10번 호출해요. 반면 FOR EACH STATEMENT로 표시된 트리거는 주어진 작업에 대해, 몇 개의 행을 수정하든지 간에 단 한 번만 실행돼요(특히 0개 행을 수정하는 작업도 적용 가능한 FOR EACH STATEMENT 트리거는 실행돼요).

트리거 이벤트 대신(INSTEAD OF) 발화하도록 지정된 트리거는 FOR EACH ROW로 표시되어야 하고, 뷰에만 정의할 수 있어요. 뷰의 BEFORE·AFTER 트리거는 FOR EACH STATEMENT로 표시되어야 해요.

또한 트리거는 TRUNCATE에 대해 발화하도록 정의할 수 있는데, FOR EACH STATEMENT로만 가능해요.

다음 표는 테이블·뷰·외부 테이블에서 어떤 트리거를 쓸 수 있는지 요약해요.

When Event Row-level Statement-level
BEFORE INSERT/UPDATE/DELETE Tables and foreign tables Tables, views, and foreign tables
TRUNCATE Tables and foreign tables
AFTER INSERT/UPDATE/DELETE Tables and foreign tables Tables, views, and foreign tables
TRUNCATE Tables and foreign tables
INSTEAD OF INSERT/UPDATE/DELETE Views
TRUNCATE

또한 트리거 정의는 Boolean WHEN 조건을 지정할 수 있는데, 트리거를 발화할지 검사하는 데 쓰여요. 행 수준 트리거에서 WHEN 조건은 행의 컬럼의 이전·그리고/또는 이후 값을 검사할 수 있어요. 문장 수준 트리거도 WHEN 조건을 가질 수 있지만, 조건이 테이블의 어떤 값도 참조할 수 없어서 그렇게 유용하진 않아요.

같은 이벤트에 같은 종류의 트리거가 여러 개 정의되어 있으면 이름의 알파벳 순으로 발화돼요.

CONSTRAINT 옵션을 지정하면 이 명령은 제약 트리거(constraint trigger)를 만들어요. 이것은 SET CONSTRAINTS로 발화 시점을 조절할 수 있다는 점 외에는 일반 트리거와 같아요. 제약 트리거는 일반 테이블(외부 테이블이 아닌)의 AFTER ROW 트리거여야 해요. 트리거 이벤트를 일으킨 문장의 끝이나, 포함하는 트랜잭션의 끝에 발화할 수 있어요. 후자의 경우 지연된(deferred) 것이라고 해요. 대기 중인 지연 트리거 발화는 SET CONSTRAINTS로 즉시 일어나도록 강제할 수도 있어요. 제약 트리거는 구현하는 제약이 위반되면 예외를 일으킬 것으로 기대돼요.

REFERENCING 옵션은 전이 릴레이션(transition relation) 수집을 활성화해요. 전이 릴레이션은 현재 SQL 문이 삽입·삭제·수정한 모든 행을 포함하는 행 집합이에요. 이 기능 덕분에 트리거가 한 번에 한 행만이 아니라 그 문이 한 일의 전체 모습을 볼 수 있어요. 이 옵션은 일반 테이블(외부 테이블이 아닌)의 AFTER 트리거에서만 허용돼요. 트리거는 제약 트리거가 아니어야 해요. 또한 트리거가 UPDATE 트리거면 이 옵션을 쓸 때 column_name 목록을 지정하면 안 돼요. OLD TABLE은 한 번만 지정할 수 있고, UPDATEDELETE에서 발화할 수 있는 트리거에서만 지정할 수 있어요. 그 문이 갱신·삭제한 모든 행의 이전 이미지(before-image)를 포함하는 전이 릴레이션을 만들어요. 비슷하게 NEW TABLE도 한 번만 지정할 수 있고, UPDATEINSERT에서 발화할 수 있는 트리거에서만 지정할 수 있어요. 그 문이 갱신·삽입한 모든 행의 이후 이미지(after-image)를 포함하는 전이 릴레이션을 만들어요.

SELECT는 행을 수정하지 않으므로 SELECT 트리거를 만들 수 없어요. SELECT 트리거가 필요한 것처럼 보이는 문제에는 규칙(rule)이나 뷰가 해결책이 될 수 있어요.

트리거에 대한 더 자세한 내용은 37장을 참고하세요.

출처: PostgreSQL 문서

본문

Synopsis

CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
    ON table_name
    [ FROM referenced_table_name ]
    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
    [ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]
    [ FOR [ EACH ] { ROW | STATEMENT } ]
    [ WHEN ( condition ) ]
    EXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )

where event can be one of:

    INSERT
    UPDATE [ OF column_name [, ... ] ]
    DELETE
    TRUNCATE

Description

CREATE TRIGGER는 새 트리거를 만들어요. CREATE OR REPLACE TRIGGER는 새 트리거를 만들거나 기존 트리거를 대체해요. 트리거는 지정한 테이블·뷰·외부 테이블에 연결되고, 그 테이블에서 특정 작업이 수행될 때 지정한 함수 function_name을 실행해요.

기존 트리거의 현재 정의를 대체하려면 CREATE OR REPLACE TRIGGER를 쓰고, 기존 트리거의 이름과 부모 테이블을 지정해요. 다른 모든 속성은 대체돼요.

트리거는 행에 대한 작업이 시도되기 전(제약 조건이 확인되고 INSERT·UPDATE·DELETE가 시도되기 전), 작업이 완료된 후(제약 조건이 확인되고 INSERT·UPDATE·DELETE가 완료된 후), 또는 작업 대신(뷰에 대한 삽입·갱신·삭제의 경우) 발화하도록 지정할 수 있어요. 트리거가 이벤트 전이나 대신 발화하면, 트리거가 현재 행에 대한 작업을 건너뛰거나 삽입될 행을 바꿀 수 있어요(INSERT·UPDATE 작업만). 트리거가 이벤트 후에 발화하면 다른 트리거의 효과를 포함한 모든 변경이 트리거에 "보여요".

FOR EACH ROW로 표시된 트리거는 작업이 수정하는 각 행마다 한 번씩 호출돼요. 예를 들어 10개 행에 영향을 주는 DELETE는 대상 릴레이션의 ON DELETE 트리거를 삭제된 행마다 각각 10번 호출하게 해요. 반면 FOR EACH STATEMENT로 표시된 트리거는 주어진 작업에 대해, 몇 개의 행을 수정하든지 간에 단 한 번만 실행돼요(특히 0개 행을 수정하는 작업도 적용 가능한 FOR EACH STATEMENT 트리거를 여전히 실행하게 해요).

트리거 이벤트 대신(INSTEAD OF) 발화하도록 지정된 트리거는 FOR EACH ROW로 표시되어야 하고, 뷰에만 정의할 수 있어요. 뷰의 BEFORE·AFTER 트리거는 FOR EACH STATEMENT로 표시되어야 해요.

또한 트리거는 TRUNCATE에 대해 발화하도록 정의할 수 있는데, FOR EACH STATEMENT로만 가능해요.

다음 표는 테이블·뷰·외부 테이블에서 어떤 종류의 트리거를 쓸 수 있는지 요약해요.

When Event Row-level Statement-level
BEFORE INSERT/UPDATE/DELETE Tables and foreign tables Tables, views, and foreign tables
TRUNCATE Tables and foreign tables
AFTER INSERT/UPDATE/DELETE Tables and foreign tables Tables, views, and foreign tables
TRUNCATE Tables and foreign tables
INSTEAD OF INSERT/UPDATE/DELETE Views
TRUNCATE

또한 트리거 정의는 Boolean WHEN 조건을 지정할 수 있어요. 트리거를 발화해야 하는지 검사하는 데 쓰이죠. 행 수준 트리거에서 WHEN 조건은 행의 컬럼의 이전·그리고/또는 이후 값을 검사할 수 있어요. 문장 수준 트리거도 WHEN 조건을 가질 수 있지만, 조건이 테이블의 어떤 값도 참조할 수 없어서 그렇게 유용하진 않아요.

같은 이벤트에 같은 종류의 트리거가 여러 개 정의되어 있으면 이름의 알파벳 순으로 발화돼요.

CONSTRAINT 옵션을 지정하면 이 명령은 제약 트리거를 만들어요. 이것은 SET CONSTRAINTS로 트리거 발화 시점을 조절할 수 있다는 것 외에는 일반 트리거와 같아요. 제약 트리거는 일반 테이블(외부 테이블이 아닌)의 AFTER ROW 트리거여야 해요. 트리거 이벤트를 일으킨 문장의 끝이나 포함하는 트랜잭션의 끝에 발화할 수 있어요. 후자의 경우 지연된(deferred) 것이라고 해요. 대기 중인 지연 트리거 발화도 SET CONSTRAINTS로 즉시 일어나도록 강제할 수 있어요. 제약 트리거는 구현하는 제약이 위반되면 예외를 일으킬 것으로 기대돼요.

REFERENCING 옵션은 전이 릴레이션을 수집할 수 있게 해 줘요. 전이 릴레이션은 현재 SQL 문이 삽입·삭제·수정한 모든 행을 포함하는 행 집합이에요. 이 기능은 트리거가 한 번에 한 행만이 아니라 그 문이 한 일의 전체 모습을 볼 수 있게 해 줘요. 이 옵션은 일반 테이블(외부 테이블이 아닌)의 AFTER 트리거에서만 허용돼요. 트리거는 제약 트리거가 아니어야 해요. 또한 트리거가 UPDATE 트리거면 이 옵션을 쓸 때 column_name 목록을 지정하면 안 돼요. OLD TABLE은 한 번만 지정할 수 있고, UPDATEDELETE에서 발화할 수 있는 트리거에서만 지정할 수 있어요. 그 문이 갱신·삭제한 모든 행의 이전 이미지를 포함하는 전이 릴레이션을 만들어요. 비슷하게 NEW TABLE도 한 번만 지정할 수 있고, UPDATEINSERT에서 발화할 수 있는 트리거에서만 지정할 수 있어요. 그 문이 갱신·삽입한 모든 행의 이후 이미지를 포함하는 전이 릴레이션을 만들어요.

SELECT는 행을 수정하지 않으므로 SELECT 트리거를 만들 수 없어요. SELECT 트리거가 필요한 것처럼 보이는 문제에는 규칙과 뷰가 실행 가능한 해결책을 제공할 수 있어요.

트리거에 대한 더 자세한 내용은 37장을 참고하세요.

Parameters

name

새 트리거에 줄 이름이에요. 같은 테이블의 다른 트리거 이름과 달라야 해요. 이름은 스키마 한정할 수 없어요 — 트리거는 테이블의 스키마를 상속하죠. 제약 트리거의 경우, SET CONSTRAINTS로 트리거 동작을 수정할 때 사용하는 이름이기도 해요.

BEFORE AFTER INSTEAD OF

함수를 이벤트 전, 후, 대신 어느 시점에 호출할지 결정해요. 제약 트리거는 AFTER로만 지정할 수 있어요.

event

INSERT, UPDATE, DELETE, TRUNCATE 중 하나로, 트리거를 발화시킬 이벤트를 지정해요. 전이 릴레이션을 요청하는 경우를 제외하고, OR로 여러 이벤트를 지정할 수 있어요.

UPDATE 이벤트의 경우 다음 문법으로 컬럼 목록을 지정할 수 있어요.

UPDATE OF column_name1 [, column_name2 ... ]

트리거는 나열된 컬럼 중 적어도 하나가 UPDATE 명령의 대상으로 언급되거나, 나열된 컬럼 중 하나가 UPDATE의 대상인 컬럼에 의존하는 생성 컬럼인 경우에만 발화해요.

INSTEAD OF UPDATE 이벤트는 컬럼 목록을 허용하지 않아요. 전이 릴레이션을 요청할 때도 컬럼 목록을 지정할 수 없어요.

table_name

트리거가 적용되는 테이블·뷰·외부 테이블의 이름(스키마 한정 가능)이에요.

referenced_table_name

제약이 참조하는 다른 테이블의 (스키마 한정 가능한) 이름이에요. 이 옵션은 외래 키 제약에 쓰이고 일반 용도로는 권장하지 않아요. 제약 트리거에서만 지정할 수 있어요.

DEFERRABLE NOT DEFERRABLE INITIALLY IMMEDIATE INITIALLY DEFERRED

트리거의 기본 시점이에요. 이 제약 옵션들의 자세한 내용은 CREATE TABLE 문서를 참고하세요. 제약 트리거에서만 지정할 수 있어요.

REFERENCING

이 키워드는 트리거 이벤트 문의 전이 릴레이션에 접근을 제공하는 릴레이션 이름 한두 개의 선언 바로 앞에 와요.

OLD TABLE NEW TABLE

이 절은 뒤따르는 릴레이션 이름이 이전 이미지 전이 릴레이션용인지 이후 이미지 전이 릴레이션용인지 나타내요.

transition_relation_name

트리거 안에서 이 전이 릴레이션에 사용할 (한정되지 않은) 이름이에요.

FOR EACH ROW FOR EACH STATEMENT

트리거 함수를 트리거 이벤트가 영향을 주는 모든 행마다 한 번씩 발화할지, SQL 문당 한 번만 발화할지 지정해요. 둘 다 지정하지 않으면 FOR EACH STATEMENT가 기본값이에요. 제약 트리거는 FOR EACH ROW로만 지정할 수 있어요.

condition

트리거 함수가 실제로 실행될지 결정하는 Boolean 표현식이에요. WHEN을 지정하면 conditiontrue를 반환할 때만 함수가 호출돼요. FOR EACH ROW 트리거에서 WHEN 조건은 OLD.column_name 또는 NEW.column_name으로 각각 행 값의 이전·그리고/또는 이후 컬럼을 참조할 수 있어요. 당연히 INSERT 트리거는 OLD를, DELETE 트리거는 NEW를 참조할 수 없어요.

INSTEAD OF 트리거는 WHEN 조건을 지원하지 않아요.

현재 WHEN 표현식은 서브쿼리를 포함할 수 없어요.

제약 트리거의 경우 WHEN 조건의 평가는 지연되지 않고, 행 갱신 작업이 수행된 직후 발생한다는 점을 유의하세요. 조건이 true로 평가되지 않으면 트리거는 지연 실행을 위해 대기열에 넣어지지 않아요.

function_name

인자를 받지 않고 trigger 타입을 반환하는 것으로 선언된 사용자 제공 함수로, 트리거가 발화할 때 실행돼요.

CREATE TRIGGER 문법에서 키워드 FUNCTIONPROCEDURE는 동등하지만, 참조된 함수는 어쨌든 프로시저가 아니라 함수여야 해요. 여기서 키워드 PROCEDURE를 쓰는 것은 역사적이며 더 이상 권장하지 않아요.

arguments

트리거가 실행될 때 함수에 제공할 선택적 쉼표 구분 인자 목록이에요. 인자는 리터럴 문자열 상수예요. 단순 이름과 숫자 상수도 여기 쓸 수 있지만, 모두 문자열로 변환돼요. 트리거 함수의 구현 언어 설명을 확인해서 이 인자들을 함수 안에서 어떻게 접근할 수 있는지 알아보세요. 일반 함수 인자와 다를 수 있어요.

Notes

테이블에 트리거를 만들거나 대체하려면 사용자가 그 테이블에 대한 TRIGGER 권한이 있어야 해요. 또한 트리거 함수에 대한 EXECUTE 권한도 있어야 해요.

트리거를 제거하려면 DROP TRIGGER를 사용해요.

파티션 테이블에 행 수준 트리거를 만들면 기존 파티션 각각에 동일한 "클론" 트리거가 만들어져요. 나중에 만들어지거나 붙는 파티션에도 동일한 트리거가 생겨요. 이미 자식 파티션에 이름이 충돌하는 트리거가 있으면 CREATE OR REPLACE TRIGGER를 쓰지 않는 한 오류가 나요. 그 경우 그 트리거는 클론 트리거로 대체돼요. 파티션이 부모에서 분리되면 그 클론 트리거는 제거돼요.

컬럼별 트리거(UPDATE OF column_name 문법으로 정의된 것)는 컬럼 중 하나가 UPDATE 명령의 SET 목록에 대상으로 나열되면 발화해요. 트리거가 발화하지 않아도 컬럼 값이 바뀔 수 있어요. BEFORE UPDATE 트리거가 행 내용에 만든 변경은 고려되지 않기 때문이에요. 반대로 UPDATE ... SET x = x ... 같은 명령은 컬럼 값이 실제로는 바뀌지 않았어도 컬럼 x의 트리거를 발화시켜요.

BEFORE 트리거에서 WHEN 조건은 함수가 실행되기 직전에 평가되므로, WHEN을 쓰는 것은 트리거 함수 시작 부분에서 같은 조건을 검사하는 것과 실질적으로 다르지 않아요. 특히 조건이 보는 NEW 행은 이전 트리거들이 수정했을 수 있는 현재 값이에요. 또 BEFORE 트리거의 WHEN 조건은 NEW 행의 시스템 컬럼(예: ctid)을 검사할 수 없어요. 아직 설정되지 않았기 때문이죠.

AFTER 트리거에서 WHEN 조건은 행 갱신이 일어난 직후 평가되고, 문장 끝에 트리거를 발화시킬 이벤트를 대기열에 넣을지 결정해요. 그래서 AFTER 트리거의 WHEN 조건이 true를 반환하지 않으면 문장 끝에 이벤트를 대기열에 넣거나 행을 다시 가져올 필요가 없어요. 트리거가 소수의 행에 대해서만 발화하면 되는 경우, 많은 행을 수정하는 문장에서 상당한 속도 향상을 얻을 수 있어요.

어떤 경우에는 단일 SQL 명령이 한 종류보다 많은 트리거를 발화시킬 수 있어요. 예를 들어 ON CONFLICT DO UPDATE 절이 있는 INSERT는 삽입·갱신 두 작업을 일으킬 수 있으므로 필요에 따라 두 종류의 트리거를 모두 발화시켜요. 트리거에 제공되는 전이 릴레이션은 그 이벤트 타입에 특화되어 있어서, INSERT 트리거는 삽입된 행만 보고 UPDATE 트리거는 갱신된 행만 봐요.

ON UPDATE CASCADEON DELETE SET NULL 같은 외래 키 강제 작업에 의한 행 갱신·삭제는 그것을 일으킨 SQL 명령의 일부로 취급돼요(그런 작업은 절대 지연되지 않는다는 점을 유의하세요). 영향을 받는 테이블의 관련 트리거가 발화되므로, 이것은 SQL 명령이 자신의 타입과 직접 일치하지 않는 트리거를 발화시킬 수 있는 또 다른 경로를 제공해요. 단순한 경우, 전이 릴레이션을 요청하는 트리거는 단일 원본 SQL 명령이 원인인 테이블의 모든 변경을 단일 전이 릴레이션으로 봐요. 하지만 전이 릴레이션을 요청하는 AFTER ROW 트리거가 있으면 단일 SQL 명령이 일으킨 외래 키 강제 작업이 각각 자신의 전이 릴레이션을 가진 여러 단계로 나뉘는 경우도 있어요. 그런 경우 문장 수준 트리거는 전이 릴레이션 집합이 생성될 때마다 한 번씩 발화되어, 트리거가 각 영향 행을 전이 릴레이션에서 정확히 한 번씩 보게 해요.

뷰의 문장 수준 트리거는 뷰에 대한 작업이 행 수준 INSTEAD OF 트리거로 처리될 때만 발화돼요. 작업이 INSTEAD 규칙으로 처리되면, 규칙이 내는 문장들이 뷰를 지명한 원래 문장을 대신해 실행돼요. 그래서 발화되는 트리거는 대체 문장에 이름이 나타난 테이블들의 트리거예요. 비슷하게, 뷰가 자동으로 갱신 가능하면 작업이 뷰의 베이스 테이블에 대한 작업으로 자동 다시 쓰여 처리되므로, 베이스 테이블의 문장 수준 트리거가 발화돼요.

파티션 테이블이나 상속 자식이 있는 테이블을 수정하면 명시적으로 이름이 붙은 테이블에 연결된 문장 수준 트리거는 발화하지만, 파티션·자식 테이블의 문장 수준 트리거는 발화하지 않아요. 반대로 행 수준 트리거는 쿼리에서 명시적으로 이름이 붙지 않더라도 영향 받는 파티션·자식 테이블의 행에 대해 발화돼요. 문장 수준 트리거가 REFERENCING 절로 이름이 붙은 전이 릴레이션과 함께 정의되었다면, 행의 이전·이후 이미지가 모든 영향 받는 파티션·자식 테이블에서 보여요. 상속 자식의 경우 행 이미지는 트리거가 연결된 테이블에 있는 컬럼만 포함해요.

현재 전이 릴레이션을 가진 행 수준 트리거는 파티션이나 상속 자식 테이블에 정의할 수 없어요. 또한 파티션 테이블의 트리거는 INSTEAD OF일 수 없어요.

현재 OR REPLACE 옵션은 제약 트리거에 지원되지 않아요.

트리거의 테이블에 대해 이미 갱신 작업을 수행한 트랜잭션 안에서 기존 트리거를 대체하는 것은 권장하지 않아요. 이미 내려진 트리거 발화 결정이나 발화 결정의 일부는 재고되지 않으므로, 그 효과가 놀랍게 느껴질 수 있어요.

자신만의 트리거 코드를 쓰지 않고도 흔한 문제를 해결할 수 있는 내장 트리거 함수가 몇 개 있어요. 9.29절을 참고하세요.

Examples

테이블 accounts의 행이 갱신되려 할 때마다 함수 check_account_update를 실행해 볼게요.

CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();

그 트리거 정의를, balance 컬럼이 UPDATE 명령에서 대상으로 지정된 경우에만 함수를 실행하도록 수정해 볼게요.

CREATE OR REPLACE TRIGGER check_update
    BEFORE UPDATE OF balance ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();

이 형태는 컬럼 balance가 실제로 값을 바꾼 경우에만 함수를 실행해요.

CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.balance IS DISTINCT FROM NEW.balance)
    EXECUTE FUNCTION check_account_update();

accounts의 갱신을 로그로 남기는 함수를, 뭔가 바뀐 경우에만 호출해 볼게요.

CREATE TRIGGER log_update
    AFTER UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.* IS DISTINCT FROM NEW.*)
    EXECUTE FUNCTION log_account_update();

뷰 밑에 깔린 테이블들에 행을 삽입하기 위해, 각 행에 대해 함수 view_insert_row를 실행해 볼게요.

CREATE TRIGGER view_insert
    INSTEAD OF INSERT ON my_view
    FOR EACH ROW
    EXECUTE FUNCTION view_insert_row();

transfer 행들이 순합계 0으로 상쇄되는지 확인하기 위해, 각 문장에 대해 함수 check_transfer_balances_to_zero를 실행해 볼게요.

CREATE TRIGGER transfer_insert
    AFTER INSERT ON transfer
    REFERENCING NEW TABLE AS inserted
    FOR EACH STATEMENT
    EXECUTE FUNCTION check_transfer_balances_to_zero();

변경이 (같은 문장으로) 동시에 짝을 이루어 일어나는지 확인하기 위해, 각 행에 대해 함수 check_matching_pairs를 실행해 볼게요.

CREATE TRIGGER paired_items_update
    AFTER UPDATE ON paired_items
    REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab
    FOR EACH ROW
    EXECUTE FUNCTION check_matching_pairs();

37.4절에는 C로 작성된 트리거 함수의 완전한 예시가 있어요.

Compatibility

PostgreSQL의 CREATE TRIGGER 문은 SQL 표준의 부분 집합을 구현해요. 현재 빠져 있는 기능은 다음과 같아요.

  • AFTER 트리거의 전이 테이블 이름은 표준 방식대로 REFERENCING 절로 지정하지만, FOR EACH ROW 트리거에서 쓰는 행 변수는 REFERENCING 절로 지정할 수 없어요. 이들은 트리거 함수가 작성된 언어에 의존하는 방식으로 사용 가능하지만, 어떤 한 언어에서는 고정돼 있어요. 일부 언어는 OLD ROW AS OLD NEW ROW AS NEW를 포함한 REFERENCING 절이 있는 것처럼 사실상 동작해요.
  • 표준은 전이 테이블을 컬럼별 UPDATE 트리거와 함께 쓰는 것을 허용하지만, 그 경우 전이 테이블에 보여야 할 행 집합은 트리거의 컬럼 목록에 의존해요. 이것은 PostgreSQL이 현재 구현하지 않아요.
  • PostgreSQL은 트리거 동작으로 사용자 정의 함수의 실행만 허용해요. 표준은 CREATE TABLE 같은 다른 SQL 명령을 여럿 트리거 동작으로 실행하는 것을 허용해요. 이 제한은 원하는 명령을 실행하는 사용자 정의 함수를 만들어 우회하기 어렵지 않아요.

SQL은 여러 트리거가 생성 시간 순서대로 발화해야 한다고 지정해요. PostgreSQL은 이름 순서를 쓰는데, 더 편리하다고 판단했어요.

SQL은 계단식 삭제에서 BEFORE DELETE 트리거가 계단식 DELETE가 완료된 후 발화해야 한다고 지정해요. PostgreSQL 동작은 BEFORE DELETE가 계단식이어도 항상 삭제 작업 전에 발화하는 거예요. 더 일관적이라고 여겨져요. 참조 작업이 일으킨 갱신 중에 BEFORE 트리거가 행을 수정하거나 갱신을 막으면 비표준 동작도 있어요. 이는 제약 위반이나 참조 제약을 따르지 않는 저장 데이터로 이어질 수 있어요.

단일 트리거에 OR로 여러 작업을 지정할 수 있는 능력은 SQL 표준에 대한 PostgreSQL 확장이에요.

TRUNCATE에 대한 트리거를 발화할 수 있는 능력과 뷰에 문장 수준 트리거를 정의할 수 있는 능력은 SQL 표준에 대한 PostgreSQL 확장이에요.

CREATE CONSTRAINT TRIGGER는 SQL 표준에 대한 PostgreSQL 확장이에요. OR REPLACE 옵션도 마찬가지예요.

더 알아보기 (Learn more)

  • DROP TRIGGER — 트리거를 제거할 때 써요.
  • ALTER TRIGGER — 트리거의 이름을 바꿀 때 써요.
  • CREATE FUNCTION — 트리거 함수를 만들 때 써요.
  • Triggers — 트리거 전반을 다루는 37장이에요.
  • CREATE TABLE — 트리거를 정의할 테이블을 만들 때 써요.