DB/MySQL

이것이MySQL이다 - 03) 스토어드 프로그램

록's 2023. 2. 10. 12:38
728x90
반응형

트리거

 

트리거(Trigger)의 개요

트리거란?
  • 사전적 의미로 ‘방아쇠’
  • 방아쇠 당기면 ‘자동’으로 총알이 나가듯이 테이블에 무슨 일이 일어나면 ‘자동’으로 실행
  • 제약 조건과 더불어 데이터 무결성을 위해 MySQL에서 사용할 수 있는 기능
  • 테이블에 DML문(Insert, Update, Delete 등) 이벤트가 발생될 때 작동
  • 테이블에 부착되는 프로그램 코드
  • 직접 실행 불가
    • 테이블에 이벤트 일어나야 자동 실행
  • IN, OUT 매개 변수를 사용할 수 없음
  • MySQL은 View에 트리거 부착 불가

 

 

트리거 실습

  • testDB에 테이블 생성
-- 트리거 : 테이블에 DML(i, u, d)문이 실행 될때 자동으로 SQL문을 실행
create database if not exists testDB;
use testdb;

create table if not exists testTbl (id int, txt varchar(10));

insert into testTbl values (1, '레드벨벳');
insert into testTbl values (2, '잇지');
insert into testTbl values (3, '블랙핑크');

 

  • testTbl에 트리거 부착
-- 트리거 부착
delimiter //
create trigger testTrg		-- 트리거 이름
	after delete			-- 삭제 후에 작동하도록 지정
	on testTbl				-- 트리거를 부착할 테이블
    for each row			-- 각 행마다 적용시킴
begin
	set @msg = '가수 그룹이 삭제됨'; -- 트리거실행 시 작동되는 코드들
end //
delimiter ;

 

 

  • 데이터 삽입, 수정, 삭제
-- 데이터를 삽입, 수정, 삭제
set @msg = '';
insert into testTbl values(4, '마마무');		-- 삽입시 아무일도 일어나지 않는다.
select @msg;
update testTbl set txt = '블핑' where ID =3;	-- 데이터 수정 시 아무일도 일어나지 않는다.
select @msg;
delete from testTbl where id = 4;		-- 데이터 삭제시 트리거 작업이 일어나게 된다.
select @msg;

 

실습 테이블 다르게 해서 다시 해보기

-- --------------------------------
create database if not exists testDB;
use testdb;

create table if not exists Trgtable (id int, txt varchar(10));

insert into Trgtable values (1, '레드벨벳');
insert into Trgtable values (2, '잇지');
insert into Trgtable values (3, '블랙핑크');

-- delete
delimiter //
create trigger Trg3		
	after delete
	on Trgtable
    for each row
begin
	set @msg = '가수 그룹이 삭제됨'; 
end //
delimiter ;

-- insert
delimiter //
create trigger Trg4		
	after insert
	on Trgtable
    for each row
begin
	set @msg = '가수 그룹이 추가됨'; 
end //
delimiter ;


-- update
delimiter //
create trigger Trg2		
	after update
	on Trgtable		
    for each row
begin
	set @msg = '가수 그룹이 업데이트됨'; 
end //
delimiter ;

set @msg = '';
insert into Trgtable values(4, '마마무');		-- 삽입시 아무일도 일어나지 않는다.
select @msg;
update Trgtable set txt = '블핑' where ID =3;	-- 데이터 수정 시 아무일도 일어나지 않는다.
select @msg;
delete from Trgtable where id = 4;		-- 데이터 삭제시 트리거 작업이 일어나게 된다.
select @msg;

 

 

 

트리거의 종류

AFTER 트리거
  • 테이블에 INSERT, UPDATE, DELETE 등의 작업이 일어났을 때 작동
  • 이름이 뜻하는 것처럼 해당 작업 후에(After) 작동
 
BEFORE 트리거
  • BEFORE 트리거는 이벤트가 발생하기 전에 작동
  • INSERT, UPDATE, DELETE 세 가지 이벤트로 작동

 

 

트리거의 사용

트리거 문법

 

 

AFTER 트리거의 사용
  • 예제 요구 사항
    • 회원 테이블에 update나 delete를 시도하면 수정 또는 삭제된 데이터를 별도의 테이블에 보관하고 변경된 일자와 변경한 사람을 기록
 
  • insert나 update 작업이 일어나는 경우, 변경되기 전의 데이터를 저장할 테이블 생성

 

-- AFTER 트리거 사용
-- 회원 테이블에 update, delete를 시도하면 수정 또는 삭제된 데이터를 별도의 테이블에 보관하고 변경된 일자와 변경한 사람을 기록

use sqldb;

drop table buytbl;	-- 구매 테이블은 필요 없으므로 삭제

create table backup_usertbl
(
	userID char(8) not null primary key,
    name varchar(10) not null,
    birthYear int not null,
    addr char(2) not null,
    mobile1 char(3),
    mobile2 char(8),
    height smallint,
    mDate date,
    modType char(2),	-- 변경된 타입. 수정 또는 삭제
    modDate date, 		-- 변경된 날짜
    modUser varchar(256) -- 변경한 사용자
);

 

 

AFTER 트리거의 사용
  • 변경(Update) 발생시 작동하는 backUserTbl_UpdateTrg 트리거 생성
-- 변경(update)이 발생할 때 작동하는 트리거
drop trigger if exists backUserTbl_UpdateTrg;


delimiter //
create trigger backUserTbl_UpdateTrg 	-- 트리거 이름
	after update	-- 변경 후에 작동하도록 지정
    on userTbl	 	-- 트리거를 부착할 테이블
    for each row	
begin
	insert into backup_userTbl values(OLD.userID, OLD.name, OLD.birthYear,
		OLD.addr, OLD.mobile1, OLD.mobile2, OLD.height, OLD.mDate,
			'수정', curdate(), current_user() );
end //
delimiter ;

 

 

AFTER 트리거의 사용
  • 삭제(Delete) 발생시 작동하는  backUserTbl_DeleteTrg 트리거 생성

 

AFTER 트리거의 사용
  • 데이터 업데이트 및 삭제
-- 삭제(delete)가 발생할 때 작동하는 트리거
drop trigger if exists backUserTbl_DeleteTrg;


delimiter //
create trigger backUserTbl_DeleteTrg 	-- 트리거 이름
	after delete	-- 변경 후에 작동하도록 지정
    on userTbl	 	-- 트리거를 부착할 테이블
    for each row	
begin
	insert into backup_userTbl values(OLD.userID, OLD.name, OLD.birthYear,
		OLD.addr, OLD.mobile1, OLD.mobile2, OLD.height, OLD.mDate,
			'삭제', curdate(), current_user() );
end //
delimiter ;
update userTbl set addr = '몽고' where userID = 'JKW';
delete from userTbl where height >= 177;
 

 

  • 수정 또는 삭제된 내용이 잘 보관되어 있는지 결과 확인
select * from backup_userTbl;

 

 

AFTER 트리거의 사용
  • 테이블의 모든 행 데이터 삭제
  • DELETE 대신 TRUNCATE TABLE문 사용

  • 백업 테이블 확인

 

  • TRUNCATE TABLE로 삭제 시에는 트리거가 작동하지 않음, DELETE 트리거는 DELETE문에만 작동

 

AFTER 트리거의 사용
  • INSERT 트리거를 생성
-- insert 트리거 
drop trigger if exists userTbl_insertTrg;
delimiter //
create trigger userTbl_insertTrg		-- 트리거 이름
	after insert		-- 입력 후에 작동하도록 지정
    on userTbl		-- 트리거를 부착할 테이블
    for each row
begin
	signal sqlstate '45000' 	-- 강제 에러 발생
		set message_text = '테이터의 입력을 시도했습니다. 귀하의 정보가 서버에 기록되었습니다';
end //
delimiter ;

 

 

데이터 입력
insert into usertbl values('ABC', '에비씨', 1977, '서울', '011', '1111111', 181, '2019-12-25');

  • 경고 메시지가 출력된 후에, INSERT 작업은 롤백이 되고 userTbl에는 데이터가 삽입되지 않음

 

 

트리거의 사용

AFTER 트리거의 사용
  • TRUNCATE TABLE 테이블이름
    • DELETE FROM 테이블이름문과 동일한 효과
    • DML문이 아니라 트리거를 작동시키지 않음
 
  • SIGNAL SQLSTATE ‘45000’ 문
    • 사용자가 오류를 강제로 발생시키는 함수
    • 사용자가 정의한 오류 메시지 출력
    • 사용자가 시도한 INSERT는 롤백

 

 

트리거가 생성하는 임시 테이블
  • INSERT, UPDATE, DELETE 작업이 수행되면 임시 사용하는 시스템 테이블
  • 이름은 ‘NEW’와 ‘OLD’

 

 

 

BEFORE 트리거의 사용
  • 테이블에 변경이 가해지기 전 작동
  • BEFORE 트리거 활용 예
    • BEFORE INSERT 트리거를 부착해 놓으면 입력될 데이터 값을 미리 확인해서 문제가 있을 경우에 다른 값으로 변경
  • BEFORE 트리거 실습
    • 값이 입력될 때, 출생년도의 데이터를 검사해서 데이터에 문제가 있으면 값을 변경시켜서 입력시키는 BEFORE INSERT 트리거 작성

 

 

 

BEFORE 트리거의 사용
  • 트리거 생성
-- before 트리거 : 트리거 실행 이전에 SQL 실행 - 입렵되는 데이터의 문제가 있는지 확인해서 수정
-- 1900년 이전의 데이터를 입력 되면 0 으로 표기, 현재 년도보다 더 높은 년도 입력 되면 현재 년도로 수정
-- sqldb.sql를 실행해서 sqldv 초기화
use sqldb;

drop trigger if exists userTbl_BeforeInsertTrg;

delimiter //
create trigger userTbl_BeforeInsertTrg 	-- 트리거 이름
	before insert		-- 입력 전에 작동하도록 지정
    on userTbl 			-- 트리거를 부착할 테이블
    for each row 
begin
	if new.birthYear < 1900 then			-- 1900년 이전에 연도가 입력시 0으로 표기
		set new.birthYear = 0;
	elseif new.birthYear > Year(curdate()) then	-- 현재 년도보다 더 큰 미래 년도가 입력시 현재 년도로 변경
		set new.birthYear = year(curdate());
	end if;
end //
delimiter ;

 

  • 값 입력 (두 값 모두 출생년도에 문제 있음)
insert into userTbl values('AAA', '에이', 1877, '서울', '011', '1112222', 181, '2022-12-25');
insert into userTbl values('BBB', '비이', 2977, '경기', '011', '1113333', 171, '2019-3-25');

 

  • SELECT * FROM userTbl문으로 확인

 

 

 

 

BEFORE 트리거의 사용
  • SHOW TRIGGERS문으로 데이터베이스에 생성된 트리거 확인
show triggers from sqldb;
 
  • 트리거 삭제
drop trigger userTbl_beforeInsertTrg;

 

기타 트리거에 관한 내용

다중 트리거 (Multiple Triggers)
  • 하나의 테이블에 동일한 트리거가 여러 개 부착되어 있는 것
    • ex) AFTER INSERT 트리거 한 개 테이블에 2개 이상 부착
 
중첩 트리거 (Nested Triggers)
  • 트리거가 또 다른 트리거를 작동시키는 것

 

 

 

트리거의 작동 순서
  • 하나의 테이블에 여러 개의 트리거가 부착된 경우 트리거의 작동 순서 지정 가능
 
중첩 트리거 작동 실습
  • 연습용 DB 생성
drop database if exists triggerdb;
create database if not exists triggerdb;

 

 

 

중첩 트리거 작동 실습
  • 테이블 생성
-- 중첩 트리거 작동 실습
drop database if exists triggerdb;

create database if not exists triggerdb;

use triggerdb;

create table orderTbl 	-- 구매 테이블
(
	orderNo int auto_increment primary key, 	-- 구매 일련번호
    userId varchar(5),		-- 구매한 회원 아이디
    prodName varchar(5),	-- 구매한 물건
    orderamount int			-- 구매한 개수
);

create table prodTbl 	-- 물품 테이블
(
	prodName varchar(5), -- 물건 이름
    account int			-- 남은 물건 수량
);

create table deliverTbl		-- 배송 테이블
(
	deliverNo int auto_increment primary key, 	-- 배송 일련번호
    prodName varchar(5),	-- 배송할 물건
    account int unique		-- 배송할 물건 개수
);

 

  • 데이터 입력
insert into prodTbl values ('사과', 100);
insert into prodTbl values ('배', 100);
insert into prodTbl values ('귤', 100);

 

 

  • 구매 테이블(orderTbl)과 물품 테이블(prodTbl)에 트리거 부착
-- 물품 테이블에서 개수를 감소시키는 트리거 
-- 구매 테이블에서 구매가 있을때(insert) 물품 테이블에 개수를 감소
drop trigger if exists orderTrg;

delimiter //
create trigger orderTrg		-- 트리거 이름 : 구매 트리거
	after insert			-- orderTbl에 insert : 구매자가 물품 구매가 있을때
    on orderTbl 		-- 트리거를 부착할 테이블 
    for each row
begin
	update prodTbl set account = account - new.orderamount
		where prodName = new.prodName; -- 제품 테이블의 재고수량을 주문수량을 제외하고 재고수량 수정
end //
delimiter ;

 

-- 배송 테이블에 새 배송 건을 입력하는 트리거
drop trigger if exists prodTrg;
delimiter //
create trigger prodTrg 	-- 트리거 이름 : 물품 트리거
	after update		
	on prodTbl		-- 트리거를 부착할 테이블 
    for each row
begin
	declare orderAmount int;
    -- 주문 개수 = (변경 전의 개수 - 변경 후의 개수)
    set orderAmount = OLD.account - new.account;
    insert into deliverTbl(prodName, account)
		values(new.prodName, orderAmount);
end //
delimiter ;

 

 

 

기타 트리거에 관한 내용

중첩 트리거 작동 실습
  • 고객이 구매한 데이터 입력
insert into orderTbl values (null, 'JOHN', '배', 5);
  • 중첩 트리거가 잘 작동했는지 세 테이블 확인
select * from orderTbl;
select * from prodTbl;
select * from deliverTbl;

 

 

 

중첩 트리거 작동 실습
  • 배송 테이블(deliverTbl)의 열 이름을 변경해서 (3)번의 INSERT가 실패하도록 실습

  • 데이터 입력
 
 

 

  • 테이블 확인

 

  • 데이터가 변경되지 않았음.
  • (3)번의 INSERT가 실패하면 (1)번 INSERT, (2)번 UPDATE 모두 롤백됨

 

 

 

 

 

728x90
반응형