본문으로 건너뛰기

트리거와 이벤트

정보처리기사 목차절차형 SQL레슨 108 / 233

목차

트리거(trigger)는 표에 INSERT·UPDATE·DELETE가 일어날 때 데이터베이스가 저절로 실행하는 절차형 SQL입니다. 정해진 시각에 실행되는 이벤트(event)와는 깨우는 것이 다릅니다.

프로시저와 함수는 누군가 이름을 불러야 돕니다. 그런데 '점수를 고치면 반드시 변경 이력을 남긴다' 같은 규칙은 부르는 쪽이 한 번만 잊어도 깨집니다. 데이터를 바꾸는 프로그램이 여럿이면 모두가 기억해야 합니다. 트리거는 그 규칙을 표에 붙여 두어, 누가 어떤 경로로 데이터를 바꾸든 같은 처리가 일어나게 합니다. 아래 그림은 수강 표에 행이 들어가는 순간 트리거가 깨어나 기록 표에 행을 더하는 장면입니다.

7단계 중 1단계. 트리거는 만들어 두기만 하면 아무 일도 하지 않습니다. 수강 표에 INSERT가 일어날 때를 기다립니다.

1트리거를 만드는 꼴 — 네 자리

트리거에는 부르는 문장이 없습니다. 그래서 '언제 깨어날지'를 만들 때 적어 둡니다. 그 자리가 머리의 두 줄입니다.

CREATE TRIGGER 이름
AFTER INSERT ON 표
FOR EACH ROW
BEGIN
  실행할 문장;
END;
자리무엇을 적나보기
선언트리거 이름CREATE TRIGGER 수강_등록기록
이벤트언제 깨어나는지 — 시점·변경 문장·표AFTER INSERT ON 수강
범위행마다인지 문장마다인지FOR EACH ROW
시작·종료깨어나서 할 일BEGIN … END;
  • 이벤트 자리의 시점은 BEFORE 또는 AFTER입니다. 변경 문장 자리에는 INSERT·UPDATE·DELETE를 쓰고, OR로 여럿을 묶어도 됩니다.
  • FOR EACH ROW가 있으면 바뀐 행마다 한 번씩 깨어납니다(행 트리거). 없으면 문장 하나에 한 번만 깨어납니다(문장 트리거). 세 행을 고치는 UPDATE 하나에 행 트리거는 세 번, 문장 트리거는 한 번 돕니다.
  • 지울 때는 DROP TRIGGER 이름;입니다.
  • 트리거 몸에는 COMMIT·ROLLBACK을 쓸 수 없습니다. 쓰면 트리거를 깨운 문장이 ORA-04092 오류로 막힙니다. 트리거는 자기를 깨운 문장과 같은 트랜잭션 안에서 돌기 때문입니다.

2:NEW와 :OLD — 바뀌는 행을 가리키는 이름

행 트리거는 지금 바뀌는 행의 값을 볼 수 있습니다. 그 행을 가리키는 이름이 둘입니다. 앞에 콜론을 붙이고 :NEW.학번처럼 칸 이름을 점으로 잇습니다.

변경 문장:OLD:NEW
INSERT값이 없다들어가는 행
UPDATE고치기 전 행고친 뒤 행
DELETE지워지는 행값이 없다
  • 들어오는 쪽이 :NEW, 나가는 쪽이 :OLD입니다. INSERT에는 나가는 행이 없고 DELETE에는 들어오는 행이 없습니다.
  • BEFORE 행 트리거에서는 :NEW에 값을 담을 수 있습니다. 저장되기 전이라 담은 값이 그대로 저장됩니다. 비어 있는 칸을 기본값으로 채우거나 범위를 벗어난 값을 다듬을 때 씁니다.
  • AFTER 트리거에서는 :NEW에 담지 못합니다. 이미 저장된 뒤이기 때문입니다. 담으려 하면 만들 때 ORA-04084 오류가 납니다.
  • 문장 트리거에는 :NEW·:OLD가 없습니다. 어느 한 행을 가리킬 수 없기 때문입니다. 쓰면 만들 때 ORA-04082 오류가 납니다.

3트리거와 이벤트 — 무엇이 깨우나

낱말이 겹칩니다. 트리거를 만들 때 쓰는 '이벤트'는 트리거를 깨우는 데이터 변경(AFTER INSERT ON 수강)입니다. 여기서 트리거와 견주는 '이벤트(event)'는 다른 것으로, 정해진 때가 되면 데이터베이스가 실행하는 일거리입니다.

가르는 기준트리거이벤트
깨우는 것표의 데이터 변경시각 — 한 번 또는 되풀이
데이터가 그대로면돌지 않는다때가 되면 돈다
매여 있는 곳특정 표데이터베이스
만드는 문장CREATE TRIGGERCREATE EVENT
쓰는 곳변경 이력 남기기, 값 다듬기, 규칙 지키기밤마다 정리, 주마다 집계

이벤트를 만드는 꼴입니다. Oracle에는 이 문장이 없어 돌려 볼 수 없습니다. 같은 일은 DBMS_SCHEDULER로 합니다.

CREATE EVENT 밤정리
ON SCHEDULE EVERY 1 DAY
DO
  DELETE FROM 수강기록
  WHERE 작업 = '등록';
  • ON SCHEDULE AT 시각은 그때 한 번만, ON SCHEDULE EVERY 간격은 그 간격마다 되풀이합니다.
  • DO 다음에 실행할 문장을 적습니다.

예제

표를 바꾸면 트리거가 따라 도는 것을 봅니다. 기록을 담을 표는 블록 안에서 만듭니다. 블록마다 처음 데이터에서 시작합니다 — 앞 블록이 만들거나 바꾼 것은 남아 있지 않습니다.

AFTER INSERT — 들어간 행을 기록한다

그림의 트리거입니다. 수강 표에 행이 들어가면 기록 표에 한 줄이 쌓입니다.

CREATE TABLE 수강기록 (
  학번 NUMBER,
  과목코드 VARCHAR2(10),
  작업 VARCHAR2(10)
);
CREATE TRIGGER 수강_등록기록
AFTER INSERT ON 수강
FOR EACH ROW
BEGIN
  INSERT INTO 수강기록
  VALUES (:NEW.학번,
      :NEW.과목코드, '등록');
END;
/
INSERT INTO 수강
VALUES (106, 'DS', 88);
SELECT 학번,
  과목코드,
  작업
FROM 수강기록;
학번과목코드작업
106DS등록

기록 표에 INSERT하는 문장은 어디에도 적지 않았습니다. 수강 표에 넣는 문장 하나가 트리거를 깨웠습니다.

AFTER UPDATE — :OLD와 :NEW를 함께 남긴다

운영체제(OS) 점수를 10점씩 올립니다. 세 행이 바뀌므로 행 트리거가 세 번 깨어납니다.

CREATE TABLE 점수기록 (
  학번 NUMBER,
  옛점수 NUMBER,
  새점수 NUMBER
);
CREATE TRIGGER 점수_변경기록
AFTER UPDATE ON 수강
FOR EACH ROW
BEGIN
  INSERT INTO 점수기록
  VALUES (:OLD.학번,
      :OLD.점수, :NEW.점수);
END;
/
UPDATE 수강
SET 점수 = 점수 + 10
WHERE 과목코드 = 'OS';
SELECT 학번,
  옛점수,
  새점수
FROM 점수기록
ORDER BY 학번;
학번옛점수새점수
1017585
1026070
1047585

UPDATE 한 문장에 기록이 세 줄 쌓였습니다. 행마다 한 번씩 깨어났기 때문입니다. 이 트리거에서 FOR EACH ROW를 빼면 문장 트리거가 되는데, 몸이 :OLD·:NEW를 쓰고 있어 만들 때 ORA-04082 오류가 납니다. 문장마다 한 번만 돌게 하려면 몸에서 :OLD·:NEW를 쓰지 않아야 합니다.

BEFORE INSERT — 저장되기 전에 값을 다듬는다

점수가 100점을 넘으면 100으로 깎아 넣습니다. 저장되기 전이라 :NEW에 값을 담을 수 있습니다.

CREATE TRIGGER 점수상한
BEFORE INSERT OR UPDATE ON 수강
FOR EACH ROW
BEGIN
  IF :NEW.점수 > 100 THEN
    :NEW.점수 := 100;
  END IF;
END;
/
INSERT INTO 수강
VALUES (106, 'OS', 120);
SELECT 학번,
  과목코드,
  점수
FROM 수강
WHERE 학번 = 106;
학번과목코드점수
106OS100

120을 넣었는데 100이 저장됐습니다. 같은 일을 AFTER 트리거로 하려 하면 만들 때 ORA-04084 오류가 납니다.

시험 포인트

  • 필기: 트리거는 데이터 변경이 일어날 때 저절로 실행됩니다. 만드는 문장은 CREATE TRIGGER이고, 머리에 시점(BEFORE·AFTER)과 변경 문장(INSERT·UPDATE·DELETE)과 표를 적습니다. FOR EACH ROW가 있으면 행 트리거(row trigger)로 바뀐 행마다, 없으면 문장 트리거(statement trigger)로 문장마다 한 번 돕니다. 바뀌는 행을 가리키는 이름은 :NEW와 :OLD이고, INSERT에는 :OLD가, DELETE에는 :NEW가 없습니다. :NEW에 값을 담는 것은 BEFORE에서만 됩니다. 트리거 몸에는 COMMIT·ROLLBACK을 쓸 수 없습니다. 이벤트와 가르는 기준은 깨우는 것이 데이터 변경인지 정해진 시각인지입니다.
  • 실기: 이름을 쓰게 하면 트리거(Trigger)와 이벤트(Event)이고, 바뀌는 행을 가리키는 이름은 :NEW와 :OLD입니다. 문장을 쓰게 하면 CREATE TRIGGER 이름·AFTER INSERT ON 표·FOR EACH ROW·BEGIN … END; 네 자리를 모두 쓰고, 바뀌는 행은 :NEW.칸이름처럼 콜론과 점까지 씁니다.

확인 문제

1. 학과 표에 새 학과를 넣을 때, 건물 칸을 비워 두고 넣으면 저장되기 전에 '본관'으로 채워 두려고 합니다. 트리거의 시점과 범위로 알맞은 것을 고르세요.

  • ① BEFORE INSERT, FOR EACH ROW 있음
  • ② BEFORE INSERT, FOR EACH ROW 없음
  • ③ AFTER INSERT, FOR EACH ROW 있음
  • ④ AFTER INSERT, FOR EACH ROW 없음
정답

①입니다. 저장되기 전에 값을 바꿔야 하므로 BEFORE이고, 행 하나하나의 건물 칸을 봐야 하므로 FOR EACH ROW가 있어야 합니다.

  • ② 시점은 맞지만 문장 트리거입니다. :NEW가 없어 어느 행의 건물 칸인지 가리킬 수 없습니다.
  • ③ 범위는 맞지만 이미 저장된 뒤입니다. AFTER에서 :NEW에 값을 담으면 만들 때 오류가 납니다.
  • ④ 두 가지가 모두 어긋납니다.

2. 다음 (가)~(라)에 맞는 말을 쓰세요. (가)·(나)는 한글 이름과 영문으로 함께, (다)·(라)는 트리거 몸에서 쓰는 꼴 그대로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.

  • (가) 표에 변경이 일어날 때 데이터베이스가 저절로 실행하는 것입니다. 부르는 문장이 따로 없고, 깨어날 때를 만들 때 적어 둡니다.
  • (나) 데이터가 그대로여도 정해진 시각이 되면 실행되는 것입니다. 특정 표에 매이지 않고 데이터베이스에 걸립니다.
  • (다) UPDATE로 깨어난 (가) 안에서 고치기 전의 값을 가리키는 이름입니다. DELETE로 깨어나면 지워지는 행을 가리킵니다.
  • (라) INSERT로 깨어난 (가) 안에서 들어가는 행을 가리키는 이름입니다. BEFORE 시점이면 여기에 값을 담아 저장될 값을 바꿀 수 있습니다.
정답

받는 답: (가) 트리거(Trigger) · (나) 이벤트(Event) · (다) :OLD · (라) :NEW

(가)의 실마리는 '변경이 일어날 때 저절로', (나)는 '정해진 시각에, 표에 매이지 않음', (다)는 '고치기 전 값·지워지는 행', (라)는 '들어가는 행·담을 수 있음'입니다.

  • 같은 이름의 다른 꼴: (다)·(라) OLD·NEW — 콜론 없이 쓴 꼴입니다. 트리거 몸에서는 :OLD·:NEW로 씁니다. 머리의 WHEN 조건 안에서만 콜론 없이 적습니다.
  • 틀리는 꼴 저장 프로시저 — (가)에: EXECUTE·CALL로 불러야 돕니다. 저절로 도는 것이 트리거입니다.
  • 틀리는 꼴 :NEW — (다)에: 들어오는 쪽입니다. UPDATE에서는 고친 뒤 값이고, DELETE에서는 값이 없습니다.
  • 틀리는 꼴 :OLD — (라)에: 나가는 쪽입니다. INSERT에서는 값이 없고, 값을 담을 수도 없습니다.

3. 학생 표에서 행이 지워질 때마다 지워진 학번과 이름을 삭제기록 표에 남기는 트리거를 쓰세요. 표를 만드는 문장부터 씁니다. 답을 끝까지 쓴 뒤 정답을 펼치세요.

정답

받는 답:

CREATE TABLE 삭제기록 (
  학번 NUMBER,
  이름 VARCHAR2(10)
);
CREATE TRIGGER 학생_삭제기록
AFTER DELETE ON 학생
FOR EACH ROW
BEGIN
  INSERT INTO 삭제기록
  VALUES (:OLD.학번,
      :OLD.이름);
END;

학번 106인 정하윤을 지우면 삭제기록에 106 · 정하윤 한 줄이 남습니다.

  • 받는 답의 폭: 트리거 이름과 칸 이름이 달라도, 이벤트가 AFTER DELETE ON 학생이고 FOR EACH ROW가 있으며 :OLD로 값을 꺼내면 맞힌 것으로 칩니다. BEFORE DELETE도 받습니다. 지워지기 전후 어느 쪽에서든 :OLD를 읽을 수 있습니다.
  • 틀리는 꼴 :NEW.학번: DELETE로 깨어난 트리거에는 들어오는 행이 없습니다. :NEW는 값이 비어 있어 기록이 빈 줄로 남습니다.
  • 틀리는 꼴 FOR EACH ROW를 뺀 꼴: 문장 트리거가 되어 :OLD를 쓸 수 없고, 여러 행을 지워도 한 번만 돕니다.
  • 틀리는 꼴 트리거 몸 끝에 COMMIT;을 둔 꼴: 트리거 안에서는 쓸 수 없습니다. DELETE 문이 ORA-04092 오류로 막힙니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

정보처리기사 목차로