18.3. 트리거 사용하기
트리거에 대한 지원은 MySQL 5.0.2에서부터 포함된다. 이 섹션에서는 트리거를 어떻게 사용하는지 그리고 트리거를 사용하는데 있어서 어떤 제약 사항이 있는지에 대해 설명하기로 한다. 트리거의 제약 사항에 대한 추가적이 내용은 Section I.1, "스토어드 루틴과 트리거상의 제약 사항"에서 제공하기로 한다.
트리거는 테이블과 연관된 이름이 있는 데이터 베이스 오브젝트이며, 테이블에 대해 특정 이벤트가 발생하면 활성화된다. 어떤 트리거는 테이블 안으로 삽입하고자 하는 값을 검사하는 기능을 수행하거나 또는 업데이트에서 호출한 값을 계산하는 기능을 수행한다.
트리거는 테이블과 관련이 있고, 테이블에 대한 INSERT, DELETE 또는 UPDATE명령문이 실행될 때에 활성화가 정의된다. 트리거는 이를 실행하는 명령문의 앞에서 또는 뒤에서 활성화 되도록 설정할 수 있다. 예를 들면, 트리거가 테이블에서 삭제되는 각 줄(row)전에 활성화 되도록 할 수 있거나 또는 각 줄이 업데이트된 후에 활성화 되도록 할 수도 있다.
트리거를 생성 또는 드롭 시키기 위해서는, CREATE TRIGGER 또는 DROP TRIGGER 명령문을 사용한다. 이러한 명령문에 대한 신텍스는 Section 18.1, "CREATE TRIGGER 신텍스", 및Section 18.2, "DROP TRIGGER 신텍스"에서 설명하였다.
INSERT명령문에 대한 테이블을 갖는 트리거와 관련된 간단한 예문을 보여주기로 한다. 이것은 테이블의 컬럼 중에 하나에 삽입된 값을 더하는 연산자의 역할을 한다.
아래의 명령문은 테이블을 생성하고 이에 해당하는 트리거를 만들게 된다:
mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
mysql> CREATE TRIGGER ins_sum BEFORE INSERT ON account
-> FOR EACH ROW SET @sum = @sum + NEW.amount;
CREATE TRIGGER 명령문은 ins_sum이라는 이름의 트리거를 생성하며 이것은 account라는 테이블과 연관된다. 이것은 또한 트리거의 동작 시간, 트리거하는 이벤트, 그리고 트리거를 활성화 하면서 해야 할 일들을 지정한다:
- 키워드 BEFORE는 트리거 실행 시간을 가리킨다. 이와 같은 경우, 트리거는 각 줄이 테이블에 삽입되기 전에 실행되어야 한다. 다른 사용 가능한 키워드는 AFTER이다.
- 키워드 INSERT는 트리거를 실행시키는 이벤트를 가리킨다. 예를 들면, INSERT 명령문은 트리거 실행을 일으킨다. 여러분은 또한 DELETE 와 UPDATE 명령문을 위한 트리거도 생성할 수가 있다.
- FOR EACH ROW에 따라오는 명령문은 트리거 실행을 일으키는 명령문을 정의하는데,트리거를 하는 명령문에 의해 영향을 받는 각 줄에 한번씩 발생하게 된다. 예문에서 보면, 트리거가 된 명령문은 amount 컬럼 안으로 삽입되는 값을 합하는 단순한 SET이 된다. 그 명령문은 "새로운 줄 속으로 삽입되어지는 amount 컬럼의 값"을 의미하는NEW.amount 의 형태로 컬럼을 참조하고 있다.
트리거를 사용하기 위해서는, 누산기 변수를 0으로 설정하고, INSERT 명령문을 실행하고, 그 다음에는 변수의 값이 어떻게 나오는지 보자:
mysql> SET @sum = 0;
mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);
mysql> SELECT @sum AS 'Total amount inserted';
+-----------------------+
| Total amount inserted |
+-----------------------+
| 1852.48 |
+-----------------------+
이와 같은 경우, INSERT명령문이 실행된 다음에 나오는 @sum 의 값은 14.98 + 1937.50 - 100, or 1852.48이 된다.
트리거를 없애기 위해서는, DROP TRIGGER 명령문을 사용한다. 만일 트리거가 디폴트 스키마에 있지 않으면, 스키마의 이름을 정확히 지정해야 한다.:
mysql> DROP TRIGGER test.ins_sum;
트리거의 이름은 스키마 이름란에 존재해야 하고, 이것은 하나의 스키마에 있는 트리거들은 서로 다른 이름을 가져야 함을 의미한다. 서로 다른 스키마에 있는 트리거들은 같은 이름을 가져도 된다..
트리거가 하나의 스키마에 대해 중복되지 않는 이름을 가져야 한다는 조건에 이외에도, 트리거를 생성하는데 있어서 몇 가지 다른 제약 사항도 있다. 특히, 하나의 테이블에는 동일한 활성화 시간 및 이벤트를 갖는 두 개의 트리거를 가질 수 없다. 예를 들면, 하나의 테이블에 대해 두 개의 BEFORE INSERT 트리거 또는 두 개의 AFTER UPDATE 트리거를 가질 수 없다. 이것은 그다지 중요하지 않은 제약 사항인데, 그 이유는 FOR EACH ROW 다음에BEGIN ... END 복합 명령문 구성을 사용해서 다중 명령문을 실행하는 트리거를 정의할 수 있기 때문이다. (하나의 예문이 이 섹션 후반부에 있다.)
OLD 와 NEW 키워드를 사용해서 트리거에 의해 영향을 받는 줄에 있는 컬럼을 활성화 시킬 수 있다. (OLD 와 NEW 는 대소문자 구분을 하지 않음.) INSERT 트리거에는, NEW.col_name 만을 사용할 수 있다; 여기에는 이전 줄(old row)이 없다. DELETE 트리거에는, OLD.col_name만을 사용할 수 있다; 여기에는 새로운 줄(new row)이 없다. UPDATE 트리거에는, 줄(row)이 업데이트되기 전에 그 줄에 있는 컬럼을 참조하도록 OLD.col_name 을 사용할 수 있으며, 줄이 업데이트된 후에 그 줄에 있는 컬럼을 참조하도록 NEW.col_name 을 사용할 수 있다.
OLD 로 표시된 컬럼은 읽기 전용(read-only)이다. 만일 SELECT권한이 있다면 이것을 참조할 수는 있으나, 수정할 수는 없다. NEW로 표시된 컬럼은, SELECT권한이 있다면, 참조될 수 있다. BEFORE 트리거에 있어서는, 이것에 대한 UPDATE권한이 있는 경우에는 SET NEW.col_name = value 을 가지고 그 값을 변경할 수 있다. 이것은 트리거를 사용해서 새로운 줄에 삽입될 값을 수정할 수 있거나 또는 줄을 업데이트할 수 있다는 것을 의미한다.
BEFORE 트리거에 있어서는, AUTO_INCREMENT 컬럼에 대한 NEW의 값은 0이 되며, 이 값은 실제로 새로운 기록이 삽입될 때 나오게 되는 자동 생성 시퀀스 숫자는 아니다.
OLD 와 NEW 는 트리거에 대한 MySQL의 확장 기능이다.
BEGIN ... END 명령문 구성을 사용해서, 다중 명령문을 실행하는 트리거를 정의할 수 있다.BEGIN 블록 안에서는, 조건문과 루프 같은 스토어드 루틴 안에서 사용 가능한 다른 신텍스를 사용할 수도 있다. 하지만, 스토어드 루틴에 대한 것과 마찬가지로, mysql 프로그램을 사용해서 다중 명령문을 실행하는 트리거를 정의하고자 한다면, 트리거 정의에서 ; 명령문 구획 문자를 사용하기 위해서는MYSQL명령문 구획 문자(delimiter)를 재 정의하는 것이 필요하다. 다음의 예문은 이런 점을 표현하는 것이다. 여기에서 UPDATE 트리거는 각 줄의 업데이트에 사용되는 새로운 값을 검사하도록 정의되며, 그리고 그 값이 0에서부터 100의 범위 내에 있도록 수정한다. 이것은 값이 줄을 업데이트 하기 전에 검사되어야 하기 때문에 BEFORE 트리거가 되어야 한다:
mysql> delimiter //
mysql> CREATE TRIGGER upd_check BEFORE UPDATE ON account
-> FOR EACH ROW
-> BEGIN
-> IF NEW.amount < 0 THEN
-> SET NEW.amount = 0;
-> ELSEIF NEW.amount > 100 THEN
-> SET NEW.amount = 100;
-> END IF;
-> END;//
mysql> delimiter ;
이렇게 하면 스토어드 프로시저를 개별적으로 정의하고 그 다음에 간단한 CALL명령문을 사용하는 트리거로부터 호출하는 것을 더 쉽게 할 수 있다. 이것은 또한 몇몇 트리거 내에서부터 동일한 루틴을 호출하고자 할 경우에 장점을 가지게 된다.
트리거가 활성화 될 때 실행하는 명령문에는 몇 가지 제약 사항이 존재한다:
- 트리거는 데이터를 클라이언트에 돌려 주는 스토어드 프로시저 또는 동적(dynamic)SQL을 사용하는 스토어드 프로시저를 호출하는 CALL명령문은 사용할 수 없다. (스토어드 프로시저가 OUT 또는 INOUT파라미터를 통해서 데이터를 트리거에 돌려 주는 것은 허용된다.)
- 트리거는 START TRANSACTION, COMMIT, 또는ROLLBACK과 같은 확정적(explicitly)으로 또는 암시적(implicitly)으로 트랜잭션을 시작 또는 종료하는 명령문은 사용할 수 없다.
- MySQL 5.0.10 이전 버전에서는, 트리거는 이름을 가지고 테이블 직접 참조를 할 수 없다.
MySQL은 트리거 실행 동안에 발생하는 에러를 아래와 같이 처리한다:
- BEFORE 트리거가 실패(fails)할 경우, 이에 대응하는 줄에 대한 동작은 수행되지 않는다.
- AFTER 트리거는 BEFORE트리거(존재 한다면)와 줄에 대한 동작 모두 성공적으로 실행될 경우에만 실행된다.
- BEFORE 또는 AFTER트리거의 실행 동안 발생되는 에러는 트리거 호출을 일으키는 전체 명령문의 실패를 일으킨다.
- 트랜잭션 테이블에 대해서는, 트리거의 실패(이에 따른 전체 명령문 실패)는 명령문에 의해 진행된 모든 데이터 변경의 롤백을 일으키게 된다. 비 트랜잭션 테이블(non-transactional tables)에 대해서는, 이러한 롤백은 일어날 수 없으며, 따라서 비록 명령문이 실패한다 하더라도, 에러가 발생한 시점 이전에 진행된 어떠한 데이터 변경도 그대로 유지된다.
출처 - http://www.mysqlkorea.co.kr/sub.html?mcode=manual&scode=01&m_no=20363&cat1=18&cat2=569&cat3=0&lang=k
===================================================================================
저자: Peter Gulutzan / 한동훈 역
원문: http://www.onlamp.com/pub/a/onlamp/2005/02/03/triggers.html
시험중인 새로운 기능을 이용할 수 있는 MySQL의 알파버전, MySQL 5.0은 트리거(Trigger)를 지원한다. MySQL 개발 로드맵에 약속된 기능이므로 놀라운 것은 아니지만, "MySQL은 할 수 없는 것들의 목록" 중에서 자주 언급되던 것을 사용하고, 동작하는 것을 지켜보는 것은 놀라운 경험이다.
이번 테스트를 위해 MySQL 레퍼런스 매뉴얼에서 개발 소스 트리에서 설치하기 절의 설명대로 가장 최신의 MySQL 5.0 소스를 다운로드하였다. 소스 트리에서 다운 받는 것은 MySQL 5.0 다운로드 페이지에서 받을 수 있는 것보다 최신판이며, 당연히 덜 테스트된 버전을 의미한다.
트리거 시험해보기
리눅스 쉘에서 mysql 클라이언트 프로그램을 실행했으며, 버전 5를 설치한 것이 맞는지 확인하기 위해 다음과 같은 코드를 작성하였다.
mysql> SELECT version();
+-------------------+
| version() |
+-------------------+
| 5.0.2-alpha-debug |
+-------------------+
1 row in set (0.00 sec)
버전을 확인한 후에 테스트 데이터베이스에 테이블과 트리거를 차례대로 생성하고, 트리거를 테스트하기 위해 INSERT 문을 실행해보았다.
mysql> CREATE DATABASE test_db;
Query OK, 1 row affected (0.27 sec)
mysql> USE test_db;
Database changed
mysql> CREATE TABLE t (column1 TINYINT);
Query OK, 0 rows affected (0.28 sec)
mysql> CREATE TRIGGER t_bi /* line 1 */
-> BEFORE INSERT ON t /* line 2 */
-> FOR EACH ROW /* line 3 */
-> SET @x = @x + 1; /* line 4 */
Query OK, 0 rows affected (0.00 sec)
mysql> SET @x = 0; /* line 5 */
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t VALUES (1),(NULL); /* line 6 */
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT @x; /* line 7 */
+------+
| @x |
+------+
| 2 |
+------+
1 row in set (0.01 sec)
위 예제에서 알 수 있는 것처럼 MySQL에서 트리거는 제대로 동작한다. 동작을 설명하기 위해 CREATE TRIGGER 문을 한번에 한줄씩 살펴보자.
트리거 이해하기
CREATE TRIGGER trigger_name /* line 1 */
당연히 첫번째는 CREATE TRIGGER 문과 트리거의 이름이다. 나는 테이블 이름 다음에 언더스코어(_), 그 다음에 bi, ai, bu, au, bd, ad와 같은 코드를 사용하는 방식으로 트리거 이름을 지정한다. 6개의 코드는 다음과 같은 의미를 지닌다.
BEFORE INSERT ON table_name /* line 2 */
or AFTER INSERT ON table_name
or BEFORE UPDATE ON table_name
or AFTER UPDATE ON table_name
or BEFORE DELETE ON table_name
or AFTER DELETE ON table_name
트리거가 동작할 수 있는 부분은 6곳이 있다. 트리거는 항상 테이블 하나에서 데이터 변경문이 발생할 때 동작하게 된다. 내가 작성한 BEFORE INSERT ON t 트리거는 테이블 t에 INSERT가 실행되기 전에 수행된다.
FOR EACH ROW /* line 3 */
위 문장은 내가 INSERT 문을 사용하는 각 열에 대해 동작하는 것을 지정한다. INSERT … SELECT 문을 사용하는 경우에 0개의 행이 삽입되는 경우가 발생한다면 트리거는 0번 동작하게 된다. 마찬가지로, 1,000개의 열을 삽입(INSERT)하게 되면 트리거도 1,000번 발생하게 된다. 표준 SQL 에서는 삽입되는 열의 수에 상관없이 트리거를 한번만 동작하게 하는 FOR EACH STATEMENT 문을 정의한다.
SET @x = @x + 1; /* line 4 */
마지막으로 살펴볼 것은 트리거의 "몸체"부분이다. 트리거가 동작하게 될 때 트리거 몸체 부분에 있는 문장이 실행된다. 예제의 트리거에서는 SET @x = @x + 1 문장이 실행된다. 이 코드는 트리거가 실행될 때마다 @x 변수를 증가시킨다.
다시 말해서, @x는 카운터다. 각 열에 INSERT가 발생할 때 마다 @x는 증가한다. 물론, @x의 기본값은 NULL이기 때문에 코드가 동작하기 않기 때문에 카운터를 초기화했다.
SET @x = 0; /* line 5 */
INSERT를 실행해서 트리거를 테스트할 차례다.
INSERT INTO t VALUES (1),(NULL); /* line 6 */
테이블 t에 ROW EACH ROW 트리거를 두었기 때문에 열을 삽입할 때마다 @x의 값이 증가해야한다. 따라서 다음과 같은 SELECT 문을 실행하였을 때,
SELECT @x; /* line 7 */
열을 두 개 추가했으므로 결과도 2가 되어야 한다.
트리거 길들이기
앞으로 작성할 UPDATE 트리거는 ;를 포함한 여러 개의 문장으로 되어 있기 때문에 UPDATE 트리거를 작성하기 전에 mysql 클라이언트에서 문장의 끝을 나타내는 구분자를 //로 변경할 것이다.
mysql> DELIMITER //
이제 UPDATE 트리거를 작성할 차례다.
mysql> CREATE TRIGGER t_bu
-> BEFORE UPDATE ON t
-> FOR EACH ROW
-> BEGIN
-> DECLARE CONTINUE HANDLER FOR 1264 SET new.column1 = -1;
-> SET new.column1 = new.column1 * 2;
-> END;//
Query OK, 0 rows affected (0.00 sec)
트리거 t_bu는 여러 개의 SQL 문장(compound statement)으로 되어 있으며, 각 열을 업데이트할 때마다 수행된다. 저장 프로시저에서 사용하는 복합문(compound statement)에 대한 구문은 MySQL 레퍼런스 매뉴얼을 참고하기 바란다. 위 트리거의 의미를 문장으로 옮기면 다음과 같다:
column1의 값에 2를 곱하고, 범위를 벗어남(out-of-range) 오류가 발생하면 column1의 값을 -1로 설정하라.
트리거의 몸체에는 MySQL 문장 및 함수들까지 모두 사용할 수 있으며, 뿐만 아니라, 해당 열의 이전 값을 "old"로, 새로운 값은 "new"로 참조할 수 있다.
에러가 발생할 때는 무엇을 할 수 있는가?
어떤 경우 에러가 발생할 수 있는가? 예를 들어, column1의 데이터 타입은 TINYINT이며, 이것의 최대값은 127이다. MySQL 5.0에서는 정상적인 동작환경에서는 타입에 대해 적절한 값이 들어가는지를 검사한다. 다음은 UPDATE 문은 "값의 범위를 벗어남(out of range value)" 오류를 고의로 만든다.
mysql> UPDATE t SET column1 = column1 + 100;//
Query OK, 1 row affected (0.00 sec)
Rows matched: 2 Changed: 1 Warnings: 1
테이블 t의 첫번째 열에 대해서 column1의 값이 1이고, 여기에 100을 더해서 101로 만들면 트리거가 이 값을 2배, 즉 202로 만든다. TINYINT 타입의 최대값은 127 이므로 예외 처리기가 동작하게 되며, column1의 값을 -1로 설정한다. 두번째 열에 있는 column1의 값은 NULL이므로 아무일도 발생하지 않는다. 이와 같은 동작이 테이블 t에 일어난 것을 증명하기 위해 테이블 t를 살펴보자.
mysql> SELECT * FROM t//
+---------+
| column1 |
+---------+
| -1 |
| NULL |
+---------+
2 rows in set (0.00 sec)
결론 및 주의할 점
INSERT와 UPDATE 문 양쪽에 대해 트리거가 동작하며, 트리거 몸체에는 복잡한 SQL 문장을 작성할 수 있다. BEFORE 트리거는 값을 삽입하거나 업데이트할 때 값을 읽고 변경할 수 있다. 이런 지원은 멋진 소식이다. 따라서, 여기서는 MySQL의 새로운 기능에 대해 주의할 점으로 끝을 맺겠다.
주의: MySQL 알파 버전은 불안정하다. MySQL 버그 데이터베이스(http://bugs.mysql.com/)에서 trigger* 키워드로 검색하고, 알려진 버그에 대해 주의해야 한다.
주의: MySQL 함수는 매우 엄격한 제약을 갖고 있다. 예를 들어, MySQL 함수는 테이블에서 SELECT를 수행할 수 없다. 트리거 동작은 함수 호출과 같이 동작하기 때문에 MySQL 함수와 같은 제약을 갖는다.
Peter Gulutzan은 수석 소프트웨어 아키텍트로 MySQL AB를 위해 일하고 있으며, 알버타(Alberta)주 에드몬튼(Edmonton)에 살고 있다.
출처 - http://www.hanb.co.kr/network/view.html?bi_id=1047
================================================================
이번 시간에는 MySQL트리거 사용법에 대해서 알아보도록 하겠습니다.
- CREATE TRIGGER문법
CREATE
[DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name trigger_time trigger_event
ON tbl_name FOR EACH ROW trigger_body
- 설명
- DEFINER
- DEFINER 조항은 트리거 활성화 시간에 액세스 권한을 확인할 때 사용되는 보안 컨텍스트를 결정합니다.
- trigger_time
- trigger_time는 트리거 동작 시간입니다.트리거가 수정되는 각 행의 앞 또는 뒤에 활성화한다는 것을 가리킨다 앞 또는 뒤에이 될 수 있습니다.
- trigger_event
- INSERT
- 새 행이 테이블에 삽입될 때마다 트리거가 활성화됩니다.
- UPDATE
- 행을 수정할 때마다 트리거가 활성화됩니다.
- DELETE
- 행이 테이블에서 삭제됩니다 때마다 트리거가 활성화됩니다.
- trigger_body
- 트리거가 활성화 될때 실행할 수 있는 구문을 기술.
- DB 만들기
- mysql에 root권한으로 접속후, 테스트 데이터베이스를 만들고, User를 만듭니다.
- mysql -u아이디 -p
create database test;
use test;
grant all privileges on test.* to test@localhost identified by 'test' with grant option;
[실행화면]
- 예제 테이블 만들기
- mysql -utest -ptest로 접속한다. 그다음 아래의 명령문을 실행한다.
use test;
CREATE TABLE test1(a1 INT);
CREATE TABLE test2(a2 INT);
CREATE TABLE test3(a3 INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE test4(
a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
b4 INT DEFAULT 0
);
[실행화면]
- 트리거 만들기
- 트리거 이름 "testref"이고, test1테이블의 Insert후 트리거를 동작
- test2테이블 a2에 test1에 추가한a1을 넣어 Insert를 함.
- test3테이블 test1에 추가한a1값과 같은 데이터를 지움.
- test4테이블 a4와 test1에 추가한a1이 같으면 b4에 1을 더한다.
delimiter |
CREATE TRIGGER testref BEFORE INSERT ON test1
FOR EACH ROW BEGIN
INSERT INTO test2 SET a2 = NEW.a1;
DELETE FROM test3 WHERE a3 = NEW.a1;
UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;
END;
|
delimiter ;
[실행화면]
- test3테이블에 초기 데이터 Insert
INSERT INTO test3 (a3) VALUES
(NULL), (NULL), (NULL), (NULL), (NULL),
(NULL), (NULL), (NULL), (NULL), (NULL);
- test4테이블에 초기 데이터 Insert
INSERT INTO test4 (a4) VALUES
(0), (0), (0), (0), (0), (0), (0), (0), (0), (0);
- test1테이블에 데이터 Insert
INSERT INTO test1 VALUES
(1), (3), (1), (7), (1), (8), (4), (4);
[실행화면]
- 결과 확인
select * from test1;
[실행화면]
select * from test2;
[실행화면]
select * from test3;
[실행화면]
select * from test4;
[실행화면]
어떤가요? 트리거에 정의했던데로 값들이 바뀌어 있죠!
트리거의 사용 방법에 대해서 조금은 알것 같죠!
오늘은 여기 까지 입니다.
감사합니다.
[참고 사이트]:http://dev.mysql.com/doc/refman/5.0/en/create-trigger.html
출처 - http://forum.falinux.com/zbxe/?mid=lecture_tip&page=1&document_srl=566230
'DB > MySQL' 카테고리의 다른 글
character_set_server 설정 (0) | 2012.04.05 |
---|---|
MySQL 로그 파일 관리 1 (0) | 2012.04.04 |
MySQL DATE 포맷 및 함수 (0) | 2012.04.02 |
Mysql 설치 후 초기 root 패스워드를 mysqladmin 으로 생성하기 (0) | 2012.04.01 |
Mysql auto_increment (0) | 2012.03.30 |