다음 두 테이블이 있다.
CREATE TABLE item (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
quantity INT NOT NULL
);
CREATE TABLE item_archive (
archive_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL
);
item 테이블에서 행이 삭제(DELETE) 될 때마다, 삭제된 행의 name 값을 item_archive 테이블에 복사(insert)하는 트리거를 작성하라. 아래 코드의 빈칸을 채워 완성하라.
DELIMITER $$
CREATE [ blank ] item_delete [ blank ] ON item
FOR EACH ROW
BEGIN
INSERT INTO item_archive(name) VALUES ( [ blank ] );
END;
$$
DELIMITER ;
빈칸에 들어갈 정답은 다음과 같다.
TRIGGERAFTER DELETEOLD.name완성 코드는 다음과 같다.
DELIMITER $$
CREATE TRIGGER item_delete AFTER DELETE ON item
FOR EACH ROW
BEGIN
INSERT INTO item_archive(name) VALUES (OLD.name);
END;
$$
DELIMITER ;
CREATE TRIGGER 이다. 따라서 첫 번째 빈칸은 TRIGGER 이어야 한다.DELETE 이벤트에 반응해야 한다. 또한 삭제가 발생한 뒤(after) 해당 값을 아카이브에 넣으면 안전하므로 AFTER DELETE가 적절하다. (BEFORE DELETE도 사용 가능하나 아래에 차이 설명)AFTER DELETE 이다.트리거 내부에서 참조할 수 있는 특수 레코드가 있다: NEW와 OLD 이다.
INSERT 트리거에서는 새로 들어오는 행을 NEW.col로 읽을 수 있다.UPDATE 트리거에서는 OLD.col(수정 전 값)과 NEW.col(수정 후 값) 둘 다 사용 가능하다.DELETE 트리거에서는 삭제되는 행의 기존 값을 참조할 수 있는 것은 OLD.col 뿐이다. NEW는 존재하지 않는다.문제는 삭제 시점에 name 값을 아카이브 테이블에 넣으라는 요구이므로 삭제되는 행의 name을 참조해야 한다. 따라서 OLD.name을 사용해야 한다. 이것이 세 번째 빈칸의 정답이다.
AFTER DELETE와 BEFORE DELETE의 차이BEFORE DELETE는 삭제가 실제로 수행되기 전에 실행된다. BEFORE에서 어떤 검증을 하고 삭제를 취소하는 방식(예외발생 등)을 구현할 수 있다.AFTER DELETE는 삭제가 완료된 후에 실행된다. 아카이브처럼 실제 삭제 행위를 기록(복사)하는 경우에는 AFTER가 더 직관적이며, 트랜잭션 관점에서도 동일 트랜잭션 내에서 작동한다(아래 참조).BEFORE에서도 OLD.name을 읽어 아카이브에 넣는 것이 가능하다. 다만 논리적으로 “삭제가 확정된 뒤 기록”이라는 의미로 AFTER를 더 많이 사용한다.FOR EACH ROW의 의미FOR EACH ROW는 행 단위(row-level) 트리거임을 의미한다. DELETE 한 번으로 여러 행이 삭제될 경우 이 트리거는 삭제되는 각 행마다 한 번씩 실행된다.DELETE FROM item WHERE ...로 N개 행이 삭제되면 트리거는 N번 실행되어 각 OLD.name 값을 아카이브에 삽입한다.CREATE TRIGGER 권한).deleted_at), 삭제자(deleted_by), 전체 덤프 등 추가 정보를 보관하는 것이 보통이다.INSERT INTO item_archive(name, quantity, ...) VALUES (OLD.name, OLD.quantity, ...); 처럼 확장하면 된다.INSERT INTO item (name, quantity) VALUES ('apple', 10), ('banana', 5);
DELETE FROM item WHERE name = 'apple';
SELECT * FROM item_archive;
-- 'apple' 이 item_archive.name 으로 들어간 것을 확인
;)를 사용하므로 프로시저/트리거 정의 시에는 DELIMITER를 변경해야 한다. 예:DELIMITER $$
CREATE TRIGGER item_delete AFTER DELETE ON item
FOR EACH ROW
BEGIN
INSERT INTO item_archive(name) VALUES (OLD.name);
END;
$$
DELIMITER ;
DELIMITER가 필요하지 않고 프로시저 블록 전체를 선택해 실행하면 되기도 한다. 환경에 따라 DELIMITER 처리법이 다르므로 주의해야 한다.CREATE TRIGGER item_delete AFTER DELETE ON item FOR EACH ROW ... VALUES (OLD.name); 이다.OLD.column을 사용해야 한다.AFTER DELETE는 삭제 확정 뒤 아카이브할 때 자연스럽고, 트리거는 호출 쿼리의 트랜잭션 범위를 따른다(롤백 시 함께 롤백).