PL/SQL : TRIGGER

부트캠프(END)/Oracle|2022. 7. 7. 14:04

TRIGGER : 데이터의 변경이 일어날 때 자동으로 같이 실행되는 프로시저

프로시저/수동 처리 ↔ 트리거/자동처리

 

데이터베이스에 이벤트 발생

이벤트 : 어떤 액션을 했을 때 내용이 바뀌는 것=INSERT, UPDATE, DELETE

→ 트리거 작동 〓 데이터베이스 관리의 자동화

잘 활용하면 서로 관계가 있는 여러 테이블끼리의 데이터 관리를 자동화할 수 있다.

 

같은 테이블에서는 트리거를 만들 수 없다. 

→ 항상 다른 테이블연쇄 반응을 일으키도록 만들어야 한다.

 

테이블에서만 사용이 가능하고, VIEW, 인라인 뷰, 서브쿼리 다 사용이 불가하다.

AutoCommit을 가지고 있어 insert, update, delete시 commit과 rollback이 불가능하다.

 

웹 이벤트 : 메뉴/버튼/이미지 클릭됨,... → 자바스크립트로 처리

자바 이벤트 : 윈도우(자바 자체가 처리)

DB 이벤트 : 데이터가 변경됨 → TRIGGER가 처리!

 

 

트리거의 특징?

- 자동으로만 실행된다(수동 실행 불가)

- 오라클 자체에서 실행되기 때문에 자바에서는 코드 확인이나 분석이 어렵다

- BEFORE/AFTER 

- 변경 대상을 먼저 반영한 후 관련된 테이블에도 반영한다 = AFTER***

- 관련된 테이블에 먼저 반영한 후 변경 대상에 반영한다 = BEFORE

- Transaction을 사용할 수 없다. (AutoCommit)

 

트리거 제작 형식

CREATE [OR REPLACE] TRIGEER tri_name
(BEFORE|AFTER) (INSERT|UPDATE|DELETE) ON table_name
--table_name에서 insert, update, delete가 일어났을 때 제어해라.
[FOR EACH ROW] --실행될 문장을 동시에 각 행에 적용
[DECLARE]
--변수 선언/선언할 변수가 없으면 DECLARE 생략 가능
BEGIN
--구현부 ... (적용할 테이블-자동화처리-에 대한 내용)
END;
/

'입고' 액션이 일어났을 때, '재고' 테이블에 입고된 부분을 반영해 주어라 등의 자동처리가 가능.

이 때 두 개의 테이블에 수량, 물품명, 코드번호 등 같은 내용이 있어야 처리가 가능하다.

 

***바인드 변수***

변수 중 새롭게 입력된 데이터와 이미 보유하고 있던 데이터를 구분한다.

:OLD는 데이터가 변경되기 이전에 가지고 있던 값(TRIGGER문을 실행되게 한 데이터 변경 이전의 값)이고,

:NEW는 데이터가 변경된 후 가지고 있는 값이다.

새로 입력된 데이터를 가져올 때는 :NEW, 이미 입력되어 있던 데이터를 가져올 때는 :OLD를 사용한다.

단, :NEW는 AFTER TRIGGER문에서는 사용될 수 없다. 

→ TRIGGER문이 AFTER TRIGGER 가 실행되기 전에 적용되기 때문이다.

https://stackoverflow.com/questions/10639331/new-and-old-trigger-code

--입고->수량,상품번호,금액
INSERT INTO 입고 VALUES('A0001',5,5000)
 :NEW.수량 (5)
 :NEW.상품번호 ('A0001')
 :NEW.금액 (5000)
--새로 입력된 데이터를 가져옴-> :NEW ->INSERT
--이미 입력되어 있는 데이터를 가져옴-> :OLD ->UPDATE,DELETE

 

 

간단한 입고/출고/재고 예제

 

테이블 설계

--상품
CREATE TABLE product(
  pno NUMBER,
  pname VARCHAR2(50),
  uprice NUMBER);  --unit price

--입고
CREATE TABLE input(
  pno NUMBER,
  quantity NUMBER,
  price NUMBER);

--출고
CREATE TABLE output(
  pno NUMBER,
  quantity NUMBER,
  price NUMBER);

--재고 -> 자동화 처리~
CREATE TABLE curproduct(
  pno NUMBER,
  quantity NUMBER,
  price NUMBER);
  
INSERT INTO product VALUES(100,'A',1000);
INSERT INTO product VALUES(101,'B',1500);
INSERT INTO product VALUES(102,'C',2000);
INSERT INTO product VALUES(103,'D',2500);
INSERT INTO product VALUES(104,'E',3000);
INSERT INTO product VALUES(105,'F',3500);

COMMIT;

 

입고 Trigger

CREATE OR REPLACE TRIGGER input_Trigger
AFTER INSERT ON input --input테이블에 insert된 이후에
FOR EACH ROW --각 행에 대해 처리해라
DECLARE
 v_cnt NUMBER; --이 트리거에서 쓰일 변수 선언
BEGIN
 --재고 테이블에서 pno가 일치하는 행이 있는지 검사 후 v_cnt에 대입
 --재고가 있으면 COUNT(*)=1, 없으면 0이 나올 것
 SELECT COUNT(*) INTO v_cnt
 FROM curproduct
 WHERE pno=:NEW.pno;
 IF v_cnt=0 THEN 
 --수량이 0이라면 재고 테이블에 row 추가
 INSERT INTO curproduct VALUES(:NEW.pno, :NEW.quantity, :NEW.price);
 ELSE
 --수량이 0이 아니라면 기존 수량과 금액에 가산
 UPDATE curproduct SET
 quantity = quantity+:NEW.quantity,
 price = price+:NEW.price
 WHERE pno=:NEW.pno;
 END IF;
END;
/

→ pno는 기존 curproduct 테이블에 있는 수많은 ROW의 pno를 의미하고,

:NEW.pno는 input테이블에 insert된(변화가 일어나 새로워진 대상) ROW의 pno를 의미한다.

즉, 입고 테이블에 새로 들어온 상품번호를 가져와 재고 테이블에 들어온 만큼의 수량과 총 금액을 추가해 준다.

 

출고 Trigger

CREATE OR REPLACE TRIGGER output_Trigger
AFTER INSERT ON output
FOR EACH ROW
DECLARE
 v_account NUMBER;
BEGIN
 SELECT quantity INTO v_account
 FROM curproduct
 WHERE pno=:NEW.pno;
 IF v_account=:NEW.quantity THEN --수량이 0이되면 delete할것임
  DELETE FROM curproduct
  WHERE pno=:NEW.pno;
 ELSE --상품이 남아있음
  UPDATE curproduct SET
  quantity = quantity-:NEW.quantity,
  price = price-:NEW.price
  WHERE pno=:NEW.pno;
 END IF;
END;
/

 

더보기
--상품명, 소개, poster, 금액
CREATE TABLE goods(
 no NUMBER,
 name VARCHAR2(1000) CONSTRAINT goods_name_nn NOT NULL,
 poster VARCHAR2(260) CONSTRAINT goods_poster_nn NOT NULL,
 price NUMBER,
 CONSTRAINT goods_no_pk PRIMARY KEY(no)
);

CREATE SEQUENCE goods_no_seq
 START WITH 1
 INCREMENT BY 1
 NOCYCLE
 NOCACHE;

CREATE INDEX goods_idx ON goods(name);

CREATE TABLE goods_input(
no NUMBER,
gno NUMBER,
account NUMBER,
price NUMBER,
CONSTRAINT gi_no_pk PRIMARY KEY(no),
CONSTRAINT gi_gno_fk FOREIGN KEY(gno) REFERENCES goods(no)
);

CREATE TABLE goods_output(
no NUMBER,
gno NUMBER,
account NUMBER,
price NUMBER,
CONSTRAINT go_no_pk PRIMARY KEY(no),
CONSTRAINT go_gno_fk FOREIGN KEY(gno) REFERENCES goods(no)
);

CREATE TABLE goods_now( 
--재고
no NUMBER,
gno NUMBER,
account NUMBER,
price NUMBER,
CONSTRAINT gn_no_pk PRIMARY KEY(no),
CONSTRAINT gn_gno_fk FOREIGN KEY(gno) REFERENCES goods(no)
);

CREATE SEQUENCE gi_no_seq
START WITH 1
INCREMENT BY 1
NOCYCLE
NOCACHE;

CREATE SEQUENCE gn_no_seq
START WITH 1
INCREMENT BY 1
NOCYCLE
NOCACHE;

CREATE SEQUENCE go_no_seq
START WITH 1
INCREMENT BY 1
NOCYCLE
NOCACHE;

INSERT INTO goods VALUES(
  goods_no_seq.nextval,
  '[만개MADE] 19가지 자연원료로 만든 만개한알 (40g*3입) 1팩',
  'https://recipe1.ezmember.co.kr/cache/data/goods/21/02/05/1000016391/1000016391_detail_017.jpg',
  12800);
INSERT INTO goods VALUES(
goods_no_seq.nextval,
'[만개특가] 닌자 마스터프렙 프로페셔널 초퍼 블렌딩',
'https://recipe1.ezmember.co.kr/cache/data/goods/21/06/25/1000020329/1000020329_detail_078.jpg',
89000);
INSERT INTO goods VALUES(
goods_no_seq.nextval,
'[만개특가] 영양간식 스트롱숏다리 1봉 20-32미(250g)',
'https://recipe1.ezmember.co.kr/cache/data/goods/21/07/28/1000021160/1000021160_detail_042.jpg',
8900);
INSERT INTO goods VALUES(
goods_no_seq.nextval,
'[만개특가] 라인바싸 탄산수 500ml 40입 무료배송 (플레인/레몬/자몽)',
'https://recipe1.ezmember.co.kr/cache/data/goods/20/09/38/1000012254/1000012254_detail_067.jpg',
14900);
INSERT INTO goods VALUES(
goods_no_seq.nextval,
'[美친특가] 휴비딕 꼬마곰/해마/펭귄/오리 탕온도계 (HBT-1/HBT-10HBT-20/HBT-30)',
'https://recipe1.ezmember.co.kr/cache/data/goods/22/03/13//1000026640/1000026640_detail_069.jpg',
5900);

COMMIT;
--트리거 INSERT
CREATE OR REPLACE TRIGGER input_insert_tr
AFTER INSERT ON goods_input
FOR EACH ROW
DECLARE
 --변수 선언
--이 변수에 대해 확인을 하겠다
v_cnt NUMBER; 
BEGIN
 --goods_now에 대한 자동화내용 구현
 SELECT COUNT(*) INTO v_cnt
 FROM goods_now
 WHERE gno=:NEW.gno;
 IF v_cnt=0 THEN
  INSERT INTO goods_now VALUES(gn_no_seq.nextval,:NEW.gno, :NEW.account, :NEW.price);
 ELSE
  UPDATE goods_now SET 
   account=account+:NEW.account,
   price=price+:NEW.price
  WHERE gno=:NEW.gno;
 END IF;
END;
/

--트리거 UPDATE
CREATE OR REPLACE TRIGGER input_update_tr
AFTER UPDATE ON goods_input
FOR EACH ROW
DECLARE
 --변수 선언
BEGIN
 --goods_now에 대한 자동화내용 구현
 UPDATE goods_now SET
  account=:OLD.account-:OLD.account+:NEW.account,
  price=:OLD.price-:OLD.price+:NEW.price
 WHERE gno=:NEW.gno;
END;
/

--트리거 DELETE
CREATE OR REPLACE TRIGGER input_delete_tr
AFTER INSERT ON goods_output
FOR EACH ROW
DECLARE
 --변수선언
 v_account NUMBER;
BEGIN
 --goods_now에 대한 자동화내용 구현
 SELECT account INTO v_account
 FROM goods_now
 WHERE gno=:NEW.gno;
 IF v_account=:NEW.account THEN
  DELETE FROM goods_now
  WHERE gno=:NEW.gno;
 ELSE
  UPDATE goods_now SET
  account = account-:NEW.account,
  price = price-:NEW.price
  WHERE gno=:NEW.gno;
 END IF;
END;
/
--insert해서 들어가는것, update set해서 들어가는 것 다 new값

 

'부트캠프(END) > Oracle' 카테고리의 다른 글

MyBatis : JOIN, IN  (0) 2022.08.09
PL/SQL : PROCEDURE  (0) 2022.07.06
PL/SQL : FUNCTION, CURSOR  (0) 2022.07.05

댓글()