DECLARE
v_num number := 100;
err_b EXCEPTION;
PRAGMA EXCEPTION_INIT(err_b, -20000);
BEGIN
if v_num > 1 THEN
RAISE_APPLICATION_ERROR(-20000,'바보');
END IF;
EXCEPTION
WHEN err_b THEN
dbms_output.put_line('안녕');
END;
/
raise_application_error는 사실 그냥 비정상적인 프로그램 종료를 시키는게 아니라 해당 오류번호에 대한 EXCEPTION문이 있는지 확인해 봤을때 따로 EXCEPTION문을 선언해 주지 않아 비정상적인 프로그램 종료가 되었던 것이었다. 따라서 해당 오류 번호에 대한 오류를 DECLARE절에 선언을 해주면 EXCEPTION문 WHEN 으로 빠져 정상적인 프로그램 종료가 가능하다.
오토 롤백, 커밋 같은 경우 해당 프로그램안에서 일어난 트랜잭션에 대해서만 롤백, 커밋을 수행한다. 예를 들어 raise_application_error 같은 비정상적인 프로그램 종료에 의해 생긴 자동 롤백은 해당 트랜잭션만 롤백하게 된다. 하지만 수동적으로 내가 작성한 롤백, 커밋 같은 경우 해당 세션에서 진행중인 전체 트랜잭션에 대해 롤백과 커밋을 수행한다.
- 행 단위 트리거
CREATE OR REPLACE TRIGGER restrict_salary
BEFORE
INSERT OR UPDATE OF salary ON hr.emp
FOR EACH ROW -- 여기까지 헤더
BEGIN
IF NOT :new.job_id IN ('AD_PRES','AD_VP') AND :new.salary > 15000 THEN
RAISE_APPLICATION_ERROR(-20000,'급여를 큰 값으로 사용할 수 없습니다');
END IF;
END;
/
- insert
INSERT INTO hr.emp VALUES(300,'SCOTT',10000, 'IT_PROG', 10); -- job_id가 조건에 해당하지 않지만 급여가 15000 이하 이기 때문에 정상작동
INSERT INTO hr.emp VALUES(300,'SCOTT',20000, 'IT_PROG', 10); /* 급여가 15000 이상으로 트리거 발동 , 오류 발생은 문장단위에서 실행된다. 포인트를 가지고 작동된다.
만약 해당 트리거에 rollback이 작성되어 있었다면, 앞에 insert transaction까지 취소 되었을거다. */
select * from hr.emp;

transaction 은 프로그램안에서는 하나로 묶이지만 프로그램 밖에서는 개별 transaction으로 발생된다. 따라서 프로그램 밖 개별 DML문에서 발생한 trigger에서 rollback이 발생하여도 다른 transaction에는 영향을 주지 않는다.
- 복제 트리거 만들기
<소스 테이블, 타켓 테이블 만들기>
CREATE TABLE hr.emp_target(
id NUMBER,
name VARCHAR2(30),
day TIMESTAMP DEFAULT SYSTIMESTAMP,
sal NUMBER);
CREATE TABLE hr.emp_source(
id NUMBER,
name VARCHAR2(30),
day TIMESTAMP DEFAULT SYSTIMESTAMP,
sal NUMBER);
<복제 트리거>
CREATE OR REPLACE TRIGGER emp_copy_trigger
BEFORE
DELETE OR INSERT OR UPDATE ON hr.emp_source
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO hr.emp_target(id,name,day,sal)
VALUES(:new.id, :new.name, :new.day, :new.sal);
ELSIF DELETING THEN
DELETE FROM hr.emp_target WHERE id = :old.id;
ELSIF UPDATING('sal') THEN
UPDATE hr.emp_target
SET sal = :new.sal
WHERE id = :old.id;
ELSIF UPDATING('name') THEN
UPDATE hr.emp_target
SET name = :new.name
WHERE id = :old.id;
END IF;
END;
/
<insert 해보기>
INSERT INTO hr.emp_source(id,name,day,sal) VALUES(1,'SCOTT',default,1000);
SELECT * FROM hr.emp_source;
SELECT * FROM hr.emp_target;
소스 테이블

타켓 테이블

- 뷰에만 생성하는 트리거, 테이블에 생성 못한다.
- 행트리거에서만 사용 가능하다.
- 테이블 생성
CREATE TABLE hr.new_emp
AS
SELECT employee_id, salary,job_id, department_id
FROM hr.employees;
CREATE TABLE hr.new_dept
AS
SELECT d.department_id, d.department_name, total_dept_sal
FROM (SELECT department_id, sum(salary) as total_dept_sal
FROM hr.employees
GROUP BY department_id)e, hr.departments d
WHERE e.department_id = d.department_id;
- 복합뷰 생성
CREATE OR REPLACE VIEW emp_details
AS
SELECT e.employee_id, e.salary, e.job_id, e.department_id, d.department_name, d.total_dept_sal
FROM hr.new_emp e , hr.new_dept d
WHERE e.department_id = d.department_id;
- 트리거 생성
CREATE OR REPLACE TRIGGER emp_dept_trigger
INSTEAD OF -- 오직 뷰를 위한 타이밍
INSERT OR UPDATE OR DELETE ON emp_details
FOR EACH ROW
BEGIN
IF INSERTING THEN -- 사원 추가할 경우
INSERT INTO hr.new_emp(employee_id, salary, job_id, department_id)
VALUES (:new.employee_id, :new.salary, :new.job_id,:new.department_id);
UPDATE hr.new_dept
SET total_dept_sal = total_dept_sal + :new.salary
WHERE department_id = :new.department_id;
ELSIF DELETING THEN -- 사원 삭제할 경우
DELETE FROM hr.new_emp WHERE employee_id = :old.employee_id;
UPDATE hr.new_dept
SET total_dept_sal = total_dept_sal - :old.salary
WHERE department_id = :old.department_id;
ELSIF UPDATING('salary') THEN -- 급여 변경
UPDATE hr.new_emp
SET salary = :new.salary
WHERE employee_id = :old.employee_id;
UPDATE hr.new_dept
SET total_dept_sal = total_dept_sal + (:new.salary - :old.salary)
WHERE department_id = :old.department_id ;
ELSIF UPDATING('department_id') THEN -- 부서이동
UPDATE hr.new_emp
SET department_id = :new.department_id
WHERE employee_id = :old.employee_id;
UPDATE hr.new_dept
SET total_dept_sal = total_dept_sal - :old.salary -- 이전부서에서는 전체 급여를 삭제
WHERE department_id = :old.department_id;
UPDATE hr.new_dept
SET total_dept_sal = total_dept_sal + :new.salary -- 새로운부서에는 전체급여 추가
WHERE department_id = :new.department_id;
END IF;
END emp_dept_trigger;
/
CREATE USER green
IDENTIFIED BY oracle
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTA UNLIMITED ON users;
GRANT CREATE SESSION TO green;
CREATE TABLE hr.audit_emp_sal(
name varchar2(30),
day timestamp,
id number,
old_sal number,
new_sal number);
CREATE OR REPLACE PROCEDURE update_proc(
p_id IN number)
IS
BEGIN
UPDATE hr.emp
SET salary = salary * 1.1
WHERE employee_id = p_id;
END update_proc;
/
CREATE OR REPLACE TRIGGER sal_audit
AFTER
UPDATE OF salary ON hr.emp
FOR EACH ROW
WHEN (old.salary != new.salary)
BEGIN
INSERT INTO hr.audit_emp_sal(name, day,id,old_sal,new_sal)
VALUES (USER,SYSTIMESTAMP,:new.employee_id,:old.salary,:new.salary);
END sal_audit;
/
GRANT execute ON hr.update_proc TO green;
execute hr.update_proc(100);
6-1. green 세션에서 본 hr.audit_emp_sal

6-2. hr 세션에서 본 hr.audit_emp_sal

PRAGMA AUTONOMOUS_TRANSACTION;
- 프로그램 안에서 독립 트랜잭션을 수행하기 위해 지시어
- COMMIT, ROLLBACK은 필수 입력
- 테이블 생성
CREATE TABLE hr.log_table(
username varchar2(30),
date_time timestamp,
message varchar2(100));
CREATE TABLE hr.temp_table(
n number);
- 1번 시나리오
CREATE OR REPLACE PROCEDURE log_message(
p_message IN varchar2)
IS
BEGIN
INSERT INTO log_table(username, date_time, message)
VALUES (user, systimestamp, p_message);
END log_message;
/
SELECT * FROM hr.log_table;
SELECT * FROM hr.temp_table;
BEGIN
log_message('오늘 하루도 행복하자!!');
INSERT INTO hr.temp_table(n) VALUES(1234);
log_message('미래를 위해서 열심히 공부하자!!');
ROLLBACK;
END;
/
<log_table>

<temp_table>

- 2번 시나리오
CREATE OR REPLACE PROCEDURE log_message(
p_message IN varchar2)
IS
BEGIN
INSERT INTO log_table(username, date_time, message)
VALUES (user, systimestamp, p_message);
COMMIT; -- 해당 프로시저 밖의 TRANSACTION까지 영향을 준다.
END log_message;
/
SELECT * FROM hr.log_table;
SELECT * FROM hr.temp_table;
-- 같은 session 안에서는 transaction은 전체적으로 하나로 묶인다.
BEGIN
log_message('오늘 하루도 행복하자!!');
INSERT INTO hr.temp_table(n) VALUES(1234);
log_message('미래를 위해서 열심히 공부하자!!');
ROLLBACK;
END;
/
<log_table>

<temp_table>

- 3번 시나리오
CREATE OR REPLACE PROCEDURE log_message(
p_message IN varchar2)
IS
PRAGMA AUTONOMOUS_TRANSACTION; -- 독립트랜잭션 제어, 해당 지시어를 사용할때는 프로시저 안에 COMMIT 또는 ROLLBACK을 사용해야 한다.,
BEGIN
INSERT INTO log_table(username, date_time, message)
VALUES (user, systimestamp, p_message);
COMMIT;
END log_message;
/
SELECT * FROM hr.log_table;
SELECT * FROM hr.temp_table;
-- 같은 session 안에서는 transaction은 전체적으로 하나로 묶인다.
BEGIN
log_message('오늘 하루도 행복하자!!'); -- 커밋
INSERT INTO hr.temp_table(n) VALUES(1234); -- 롤백
log_message('미래를 위해서 열심히 공부하자!!'); --커밋
ROLLBACK;
END;
/
<log_table>

<temp_table>

- TRIGGER 에서 독립 트랜잭션 처리 하는 방법
CREATE OR REPLACE TRIGGER sal_audit
AFTER
UPDATE OF salary ON hr.emp
FOR EACH ROW
WHEN (old.salary != new.salary)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO hr.audit_emp_sal(name, day,id,old_sal,new_sal)
VALUES (USER,SYSTIMESTAMP,:new.employee_id,:old.salary,:new.salary);
COMMIT;
END sal_audit;
/