트리거는 상용 데이터베이스 시스템이 무결성제약 관리를 위하여 지원하는 기능이며, SQL 표준은 1999년도 제정되었지만 상용 데이터베이스 시스템은 이전부터 자체적으로 지원해왔다.
트리거는 기본적으로 ECA 규칙으로서 사건(Event)/조건(Condition)/행동(Action) 부문으로 구성된다.
트리거를 디자인하려면 다음을 명확히 해야한다:
트리거에서 의미하는 사건은 터플의 변화이며, 터플 입력, 터플 삭제, 터플 갱신 연산을 의미한다.
after update of 속성명 on 테이블명
이다.변경 전/후의 속성 값은 참조될 수가 있다. (By 변수 선언)
referencing old row as
: delete and update 연산을 위해referencing new row as
: insert and update 연산을 위해터플에 변화가 있으면 변화하기 전 터플과 변화 후의 터플을 지칭하는 문장이 상기에 나와 있다. delete 연산이면 변화 전 터플만을 지칭할 수 있고, insert 연산이면 변화 후 터플만을 지칭할 수 있다.
수강하여 학점 취득한 상황뿐만 아니라, F였다가 성적변경으로 인해 다른 성적을 받아도 학점을 취득할 수 있다.
after update of grade on takes
: grade에 update가 된다면 trigger 발동old grade is 'F' or old grade is null
new grade is not 'F' and new grade is not null
Create trigger myCred after update of grade on takes
referencing new row as nrow
referencing old row as orow
for each row # takes에 update 대상이되는 각각 튜플에 대해서
when (nrow.grade <> ’F’ and nrow.grade is not null)
and (orow.grade = ’F’ or orow.grade is null)
begin
Update student
set totalCredit = totalCredit +
(select credit
from course
where cID = nrow.cID)
where sID = nrow.sID;
end;
첫 문장에서 트리거 이름과 사건을 명시한다.
두 번째, 세 번째 문장은 update가 발생하는 경우 update 전과 후의 테이블의 터플을 참조하는 문장이다.
네 번째 문장(for each row)은 조건과 행동을 값이 변경된 각 터플(상기 예제에서는 grade 속성 값이 변경된 터플)을 기준으로 수행하는 것을 의미한다. 즉, takes 테이블의 update된 각 터플에 대하여 조건을 검사하고 조건을 만족하면 행동(여기서는 Update 문장)을 수행한다.
다섯번쨰 문장의 when절은 조건을 명시하고,
여섯번째 문장의 조건 밑에 나오는 begin 블록이 트리거의 행동을 명시한다.
when절을 보면, 변경후 터플의 grade 값은 ‘F’가 아니면서 널 값도 아니고, 변경전 터플의 grade 값은 ‘F’이거나 널 값이어야 한다. 조건이 만족하면 begin 블록을 수행하는데, 그 내용은 student 테이블의 totalCredit 속성 갱신이다. 새로운 터플의 cID 값을 가지고 course 테이블에서 해당 과목의 credit 값을 구하여 기존 totalCredit 속성 값에 합산하는 연산을 한다.
한 사람이 카드에 돈이 없는데도 계속 돈을 썼을 때, 마이너스 통장을 만드는 대신에 은행측에서는 다음과 같이 처리한다고 가정하자:
간단한 은행 관련 스키마이다. account 테이블에 계좌번호(aNumber
)와 잔고(balance
)가 있으며, 계좌번호를 가진 고객이름(cName
)은 depositor 테이블에 저장되어 있다. 은행에서 고객이 대출을 받는 경우, loan 테이블은 대출번호(lNumber
)와 대출금이 있으며, 대출 고객명(cName
)은 borrower 테이블에 나온다.
상기 트리거는 잔고보다 더 많은 금액의 수표가 발생되는 경우(즉, overdraft) 대출 계좌를 사용자 명의로 개설하고 대출금을 overdraft된 금액으로 하는 작업을 한다.
Event는 account 테이블 갱신 연산이며, Condition은 balance 속성 값이 음수(negative)로 되는 것이다.
Create trigger myOverdraft after update of balance on account
referencing new row as nrow
for each row
when nrow.balance < 0
begin atomic
Insert into borrower
(select cName, aNumber
from depositor
where nrow.aNumber = depositor.aNumber);
Insert into loan values (nrow.aNumber, –nrow.balance);
Update account set balance = 0
where account.aNumber = nrow.aNumber;
end
상기 그림에서 주요 연산은 다음과 같다. 트리거 수행되는 조건은 account
의 balance
가 음수(negative)가 되는 것이다.
그리고 왜
referecing new row as nrow
만 했냐면 옛날의 정보와 비교할 필요 없이 오로지 업데이트연산된 balance가 오로지 음수가 되는지만 확인하면 되므로 old row는 필요없다.
첫 번째 Insert 연산은 borrower
에 새로운 터플을 생성하는데, depositor에 있는 aNumber를 loan number로 사용한다.
두 번째 Insert 연산은 loan
에 새로운 터플을 입력한다. 주어진 balance의 값에 음수를 붙여 양수 값이 되며, 이 값을 loan amount로 입력된다. 마지막 갱신 연산은 account 관계의 balance 속성 값을 zero로 갱신한다.
상기 예제에서 행동에 해당되는 insert/insert/update 문장을 트랜잭션으로 수행하기 위하여 begin atomic ... end
를 사용하였다. 데이터베이스 연산을 트랜잭션으로 처리하면 상기 예제에서는 세 개의 SQL 문장이 모두 수행하던가 아니면 하나도 수행하지 않는다 (all-or-nothing).
트랜잭션은 ACID(atomicity, consistency, isolation, durability) 성질을 가지는 데이터베이스 연산의 sequence인데 자세한 사항은 생략하기로 한다.
만약 행동 SQL 문장이 하나이면 begin atomic ... end
를 사용하지 않아도 무방하다
Create trigger myTotalSalary after update of salary on employee
referencing new row as nrow
referencing old row as orow
for each row
when (nrow.dNumber is not null)
Update department
set totalSalary=totalSalary+nrow.salary–orow.salary
where dno = nrow.dNumber;
상기 예제는 employee 관계의 salary 속성에 변경이 발생하는 경우, department 관계의 totalSalary 속성을 변경하는 터플 수준 트리거이다.
트리거 조건은 nrow.dNumber의 값이 널이 아닌 경우이다. nrow.dNumber의 값이 널이면 갱신된 근로자의 소속 부서를 모르므로 department 관계의 totalSalary 속성 값 갱신이 불가능하다.
트리거를 정의 할 때, before 키워드를 사용하면 트리거는 이벤트가 수행되기 전에 트리거가 수행된다.
Create trigger mySetNull before update of grade on takes
referencing new row as nrow
for each row
when (nrow.grade = ' ')
Update takes set nrow.grade = null;
상기 예제는 takes 테이블을 갱신하기 전에 터플의 grade 속성 값이 ‘ ’(값은 존재하나 존재하는 값이 blank)이면 이를 널 값으로 갱신하는 트리거이다.
즉, update를 할 때 null값으로 바꿔서 update하라는 구문이다.
Tuple Level Trigger(터플 수준 트리거)는 터플 단위로 트리거 행동을 수행하게 된다.
반면에,
Statement Level Trigger(문장 수준 트리거)는 터플 단위가 아니고 SQL 문장 단위로 트리거 행동을 수행하게 되며, 사건 전후 테이블을 테이블 단위로 참조한다.
for each row
대신에 for each statement
를 사용referencing old table
또는 referencing new table
사용트리거 행동으로 인하여 매우 많은 수의 터플에 변화가 있는 경우에는 문장 수준 트리거가 유용하다.
Create trigger myTotalSalaryStateLevel after update of salary on employee
referencing old table as O
referencing new table as N
for each statement
when exists(select * from O where O.dnumber is not null) or
exists(select * from N where N.dnumber is not null)
Update department as D
set D.totalSalary = D.totalSalary
+ (select sum(N.salary) from N where D.dno=N.dnumber)
- (select sum(O.salary) from O where D.dno=O.dnumber)
where D.dno in ( (select dnumber from N) union
(select dnumber from O) );
상기 예제는 employee 관계의 salary 속성에 변경이 발생하는 경우, department 관계의 totalSalary 속성을 변경하는 문장 수준 트리거이다.
when절에 명시되어 있는 조건은 구 테이블이나 신 테이블의 dNumber 속성이 널 값이 아닌 터플이 존재하면 만족하는데, 이는 널 값이 아닌 dNumber 터플이 존재하면 해당 department의 totalSalary 속성 값을 변경해야 하기 때문이다.
트리거의 실행 부문에서 각 터플별로 수행을 하지 않고 전체 테이블에 대하여 일괄적으로 totalSalary 속성 값을 변경하고 있다.