Search
Duplicate

ํŠธ๋ฆฌ๊ฑฐ

ํŠธ๋ฆฌ๊ฑฐ(Trigger)

ํŠน์ • ์ด๋ฒคํŠธ(INSERT, UPDATE, DELETE)๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ž๋™์œผ๋กœ ์‹คํ–‰๋˜๋Š” PL/SQL ๋ธ”๋ก
โ€ข
์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์‹คํ–‰ํ•˜์ง€ ์•Š์•„๋„ ๋จ
โ€ข
ํ…Œ์ด๋ธ”/๋ทฐ์—์„œ ๋ฐ์ดํ„ฐ ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ ์ž๋™ ์‹คํ–‰

ํŠธ๋ฆฌ๊ฑฐ ์‚ฌ์šฉ ์‚ฌ๋ก€ ์˜ˆ์‹œ

์‚ฌ๋ก€ 1: ์žฌ๊ณ  ์ž๋™ ์—…๋ฐ์ดํŠธ

์ฃผ๋ฌธ์ด ๋ฐœ์ƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ์žฌ๊ณ ๋ฅผ ์ฐจ๊ฐํ•˜๋Š” ํŠธ๋ฆฌ๊ฑฐ
CREATE OR REPLACE TRIGGER trg_update_stock AFTER INSERT ON orders FOR EACH ROW BEGIN UPDATE products SET stock = stock - :NEW.quantity WHERE product_id = :NEW.product_id; END; /
SQL
๋ณต์‚ฌ
ํšจ๊ณผ: ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ ์ž…๋ ฅ ์‹œ ์žฌ๊ณ  ๊ด€๋ฆฌ ํ…Œ์ด๋ธ”์ด ์ž๋™ ๊ฐฑ์‹ ๋จ

์‚ฌ๋ก€ 2: ๊ธ‰์—ฌ ๋ณ€๊ฒฝ ์ด๋ ฅ ๊ด€๋ฆฌ

์ง์›์˜ ๊ธ‰์—ฌ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋ณ€๊ฒฝ ์ด๋ ฅ์„ ์ž๋™์œผ๋กœ ๊ธฐ๋ก
CREATE OR REPLACE TRIGGER trg_salary_history AFTER UPDATE OF salary ON employees FOR EACH ROW BEGIN INSERT INTO salary_history (emp_id, old_salary, new_salary, change_date) VALUES (:OLD.emp_id, :OLD.salary, :NEW.salary, SYSDATE); END; /
SQL
๋ณต์‚ฌ
ํšจ๊ณผ: ๊ธ‰์—ฌ ๋ณ€๊ฒฝ ๋‚ด์—ญ์ด ์ž๋™์œผ๋กœ ์ถ”์ ๋˜์–ด ๊ฐ์‚ฌ ๋ฐ ๋ถ„์„์— ํ™œ์šฉ ๊ฐ€๋Šฅ

์‚ฌ๋ก€ 3: ๋ฐ์ดํ„ฐ ์ž…๋ ฅ ์‹œ ์ž๋™ ๊ฒ€์ฆ

ํšŒ์› ๊ฐ€์ž… ์‹œ ์ด๋ฉ”์ผ ํ˜•์‹์ด ์ž˜๋ชป๋˜์—ˆ์œผ๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ
CREATE OR REPLACE TRIGGER trg_validate_email BEFORE INSERT ON members FOR EACH ROW BEGIN IF :NEW.email NOT LIKE '%@%.%' THEN RAISE_APPLICATION_ERROR(-20001, '์œ ํšจํ•˜์ง€ ์•Š์€ ์ด๋ฉ”์ผ ํ˜•์‹์ž…๋‹ˆ๋‹ค.'); END IF; END; /
SQL
๋ณต์‚ฌ
ํšจ๊ณผ: ์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ํ…Œ์ด๋ธ”์— ์‚ฝ์ž…๋˜๊ธฐ ์ „์— ์ฐจ๋‹จํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ์œ ์ง€

ํŠธ๋ฆฌ๊ฑฐ์˜ ํŠน์ง•

ํŠน์ง•
์„ค๋ช…
์ž๋™ ์‹คํ–‰
์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์˜ค๋ผํด์ด ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰
DML ๊ธฐ๋ฐ˜ ์‹คํ–‰
INSERT, UPDATE, DELETE ๋™์ž‘ ์‹œ ์ž‘๋™
BEFORE / AFTER ๊ตฌ๋ถ„
๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ „ ๋˜๋Š” ํ›„ ์‹คํ–‰ ์„ค์ •
ํ–‰(ROW) ๋˜๋Š” ๋ฌธ์žฅ(STATEMENT) ๋‹จ์œ„
๊ฐ ํ–‰๋งˆ๋‹ค ์‹คํ–‰๋˜๊ฑฐ๋‚˜ SQL ๋ฌธ ํ•˜๋‚˜๋‹น ํ•œ ๋ฒˆ ์‹คํ–‰ ๊ฐ€๋Šฅ

ํŠธ๋ฆฌ๊ฑฐ ์ข…๋ฅ˜

ํŠธ๋ฆฌ๊ฑฐ ์ข…๋ฅ˜
์‹คํ–‰ ์‹œ์ 
์„ค๋ช…
BEFORE Trigger
DML ์‹คํ–‰ ์ „
๋ฐ์ดํ„ฐ ๊ฒ€์ฆ ๋ฐ ์ˆ˜์ • ๊ฐ€๋Šฅ
AFTER Trigger
DML ์‹คํ–‰ ํ›„
๋กœ๊ทธ ๊ธฐ๋ก ๋“ฑ์— ์‚ฌ์šฉ
ROW Trigger(FOR EACH ROW)
๊ฐ ํ–‰๋งˆ๋‹ค
DML๋กœ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ๊ฐ ํ–‰๋งˆ๋‹ค ์‹คํ–‰
STATEMENT Trigger
SQL ๋ฌธ๋‹น 1ํšŒ
SQL ๋ฌธ ํ•˜๋‚˜๋‹น ๋”ฑ ํ•œ ๋ฒˆ ์‹คํ–‰
INSTEAD OF Trigger
VIEW DML ์‹œ
VIEW์—์„œ DML์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ํŠธ๋ฆฌ๊ฑฐ

ํŠธ๋ฆฌ๊ฑฐ ๊ตฌ์กฐ (๊ธฐ๋ณธ ๋ฌธ๋ฒ•)

CREATE OR REPLACE TRIGGER ํŠธ๋ฆฌ๊ฑฐ์ด๋ฆ„ BEFORE | AFTER INSERT OR UPDATE OR DELETE ON ํ…Œ์ด๋ธ”๋ช… [FOR EACH ROW] BEGIN -- ์‹คํ–‰ํ•  PL/SQL ์ฝ”๋“œ END; /
SQL
๋ณต์‚ฌ

:OLD / :NEW ๊ฐ€์ƒ ๋ ˆ์ฝ”๋“œ

๊ฐ€์ƒ ๋ ˆ์ฝ”๋“œ
์„ค๋ช…
์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ DML
:OLD
๋ณ€๊ฒฝ ์ด์ „ ๊ฐ’
UPDATE, DELETE
:NEW
๋ณ€๊ฒฝ ์ดํ›„ ๊ฐ’
INSERT, UPDATE

:OLD / :NEW ์‚ฌ์šฉ ์˜ˆ์‹œ

์˜ˆ์‹œ 1: INSERT ์‹œ

CREATE OR REPLACE TRIGGER trg_insert_example AFTER INSERT ON employees FOR EACH ROW BEGIN -- INSERT ์‹œ์—๋Š” :NEW๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ DBMS_OUTPUT.PUT_LINE('์ƒˆ๋กœ ์ž…๋ ฅ๋œ ์‚ฌ์›๋ฒˆํ˜ธ: ' || :NEW.emp_id); DBMS_OUTPUT.PUT_LINE('์ƒˆ๋กœ ์ž…๋ ฅ๋œ ์ด๋ฆ„: ' || :NEW.name); DBMS_OUTPUT.PUT_LINE('์ƒˆ๋กœ ์ž…๋ ฅ๋œ ๊ธ‰์—ฌ: ' || :NEW.salary); -- :OLD๋Š” NULL (์กด์žฌํ•˜์ง€ ์•Š์Œ) END; /
SQL
๋ณต์‚ฌ

์˜ˆ์‹œ 2: UPDATE ์‹œ

CREATE OR REPLACE TRIGGER trg_update_example AFTER UPDATE ON employees FOR EACH ROW BEGIN -- UPDATE ์‹œ์—๋Š” :OLD์™€ :NEW ๋ชจ๋‘ ์‚ฌ์šฉ ๊ฐ€๋Šฅ DBMS_OUTPUT.PUT_LINE('์‚ฌ์›๋ฒˆํ˜ธ: ' || :OLD.emp_id); DBMS_OUTPUT.PUT_LINE('๋ณ€๊ฒฝ ์ „ ์ด๋ฆ„: ' || :OLD.name); DBMS_OUTPUT.PUT_LINE('๋ณ€๊ฒฝ ํ›„ ์ด๋ฆ„: ' || :NEW.name); DBMS_OUTPUT.PUT_LINE('๋ณ€๊ฒฝ ์ „ ๊ธ‰์—ฌ: ' || :OLD.salary); DBMS_OUTPUT.PUT_LINE('๋ณ€๊ฒฝ ํ›„ ๊ธ‰์—ฌ: ' || :NEW.salary); DBMS_OUTPUT.PUT_LINE('๊ธ‰์—ฌ ์ฐจ์ด: ' || (:NEW.salary - :OLD.salary)); END; /
SQL
๋ณต์‚ฌ

์˜ˆ์‹œ 3: DELETE ์‹œ

CREATE OR REPLACE TRIGGER trg_delete_example AFTER DELETE ON employees FOR EACH ROW BEGIN -- DELETE ์‹œ์—๋Š” :OLD๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ DBMS_OUTPUT.PUT_LINE('์‚ญ์ œ๋œ ์‚ฌ์›๋ฒˆํ˜ธ: ' || :OLD.emp_id); DBMS_OUTPUT.PUT_LINE('์‚ญ์ œ๋œ ์ด๋ฆ„: ' || :OLD.name); DBMS_OUTPUT.PUT_LINE('์‚ญ์ œ๋œ ๊ธ‰์—ฌ: ' || :OLD.salary); -- :NEW๋Š” NULL (์กด์žฌํ•˜์ง€ ์•Š์Œ) END; /
SQL
๋ณต์‚ฌ

์˜ˆ์‹œ 4: ๊ธ‰์—ฌ ๋ณ€๊ฒฝ ์ด๋ ฅ ์ƒ์„ธ ๊ธฐ๋ก

CREATE OR REPLACE TRIGGER trg_salary_change_detail AFTER UPDATE OF salary ON employees FOR EACH ROW BEGIN -- ๊ธ‰์—ฌ๊ฐ€ ์‹ค์ œ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ ๊ธฐ๋ก IF :OLD.salary != :NEW.salary THEN INSERT INTO salary_history ( emp_id, emp_name, old_salary, new_salary, salary_diff, change_rate, change_date ) VALUES ( :OLD.emp_id, :NEW.name, -- ๋ณ€๊ฒฝ ํ›„ ์ด๋ฆ„ :OLD.salary, -- ๋ณ€๊ฒฝ ์ „ ๊ธ‰์—ฌ :NEW.salary, -- ๋ณ€๊ฒฝ ํ›„ ๊ธ‰์—ฌ :NEW.salary - :OLD.salary, -- ๊ธ‰์—ฌ ์ฐจ์ด ROUND((:NEW.salary - :OLD.salary) / :OLD.salary * 100, 2), -- ๋ณ€๊ฒฝ๋ฅ (%) SYSDATE ); END IF; END; /
SQL
๋ณต์‚ฌ

์˜ˆ์‹œ 5: ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ ๋ฐ ์ˆ˜์ •

CREATE OR REPLACE TRIGGER trg_validate_and_modify BEFORE INSERT OR UPDATE ON employees FOR EACH ROW BEGIN -- ๊ธ‰์—ฌ๊ฐ€ ์Œ์ˆ˜๋ฉด 0์œผ๋กœ ๋ณ€๊ฒฝ IF :NEW.salary < 0 THEN :NEW.salary := 0; END IF; -- ์ด๋ฆ„์ด NULL์ด๋ฉด '๋ฏธ์ž…๋ ฅ'์œผ๋กœ ์„ค์ • IF :NEW.name IS NULL THEN :NEW.name := '๋ฏธ์ž…๋ ฅ'; END IF; -- ์ด๋ฉ”์ผ์„ ์†Œ๋ฌธ์ž๋กœ ์ž๋™ ๋ณ€ํ™˜ :NEW.email := LOWER(:NEW.email); -- ์ˆ˜์ •์ผ ์ž๋™ ๊ฐฑ์‹  :NEW.modified_date := SYSDATE; END; /
SQL
๋ณต์‚ฌ

์˜ˆ์‹œ 6: ์กฐ๊ฑด๋ถ€ ์‹คํ–‰ (ํŠน์ • ์ปฌ๋Ÿผ ๊ฐ’์— ๋”ฐ๋ผ)

CREATE OR REPLACE TRIGGER trg_high_salary_alert AFTER UPDATE OF salary ON employees FOR EACH ROW BEGIN -- ๊ธ‰์—ฌ๊ฐ€ 10000 ์ด์ƒ์œผ๋กœ ์ธ์ƒ๋œ ๊ฒฝ์šฐ์—๋งŒ ์•Œ๋ฆผ IF :NEW.salary >= 10000 AND :OLD.salary < 10000 THEN INSERT INTO high_salary_alerts ( emp_id, emp_name, new_salary, alert_date ) VALUES ( :NEW.emp_id, :NEW.name, :NEW.salary, SYSDATE ); END IF; END; /
SQL
๋ณต์‚ฌ

ํ•ต์‹ฌ ์ •๋ฆฌ

โ€ข
INSERT: :NEW๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ (์ƒˆ๋กœ ์ž…๋ ฅ๋˜๋Š” ๊ฐ’)
โ€ข
UPDATE: :OLD์™€ :NEW ๋ชจ๋‘ ์‚ฌ์šฉ ๊ฐ€๋Šฅ (๋ณ€๊ฒฝ ์ „/ํ›„ ๋น„๊ต ๊ฐ€๋Šฅ)
โ€ข
DELETE: :OLD๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ (์‚ญ์ œ๋˜๋Š” ๊ธฐ์กด ๊ฐ’)
โ€ข
BEFORE ํŠธ๋ฆฌ๊ฑฐ์—์„œ๋Š” :NEW ๊ฐ’์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Œ

ํŠธ๋ฆฌ๊ฑฐ ์˜ˆ์ œ

(1) INSERT ์‹œ ์ž๋™ ๋กœ๊ทธ ๊ธฐ๋ก

CREATE OR REPLACE TRIGGER trg_emp_log AFTER INSERT ON employees FOR EACH ROW BEGIN INSERT INTO emp_log (emp_id, action_date, action_type) VALUES (:NEW.emp_id, SYSDATE, 'INSERT'); END; /
SQL
๋ณต์‚ฌ

(2) UPDATE ์ „ ๊ฐ’ ๋ณ€๊ฒฝ(๊ธ‰์—ฌ๊ฐ€ 0 ๋ฏธ๋งŒ์ด๋ฉด 0์œผ๋กœ)

CREATE OR REPLACE TRIGGER trg_check_salary BEFORE UPDATE ON employees FOR EACH ROW BEGIN IF :NEW.salary < 0 THEN :NEW.salary := 0; END IF; END; /
SQL
๋ณต์‚ฌ

(3) DELETE ์‹œ ์‚ญ์ œ ๋กœ๊ทธ ๊ธฐ๋ก

CREATE OR REPLACE TRIGGER trg_delete_log AFTER DELETE ON employees FOR EACH ROW BEGIN INSERT INTO emp_delete_log (emp_id, del_date) VALUES (:OLD.emp_id, SYSDATE); END; /
SQL
๋ณต์‚ฌ

ํŠธ๋ฆฌ๊ฑฐ ํ™•์ธ ๋ฐ ์‚ญ์ œ

ํŠธ๋ฆฌ๊ฑฐ ์กฐํšŒ

SELECT trigger_name, status FROM user_triggers;
SQL
๋ณต์‚ฌ

ํŠธ๋ฆฌ๊ฑฐ ์‚ญ์ œ

DROP TRIGGER ํŠธ๋ฆฌ๊ฑฐ์ด๋ฆ„;
SQL
๋ณต์‚ฌ

ํŠธ๋ฆฌ๊ฑฐ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

โ€ข
๊ณผ๋„ํ•œ ๋กœ์ง โ†’ ์„ฑ๋Šฅ ์ €ํ•˜ ์›์ธ
โ€ข
๋””๋ฒ„๊น… ์–ด๋ ค์›€ โ†’ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ•„์ˆ˜
โ€ข
์ˆœํ™˜ ํ˜ธ์ถœ ๋ฐœ์ƒ ๊ฐ€๋Šฅ โ†’ ๋™์ผ ํ…Œ์ด๋ธ” UPDATE ์‹œ ์ฃผ์˜

ํŠธ๋ฆฌ๊ฑฐ ์‚ฌ์šฉ ๋ชฉ์  ์ •๋ฆฌ

โ€ข
๊ฐ์‚ฌ ๋กœ๊ทธ(Audit)
โ€ข
๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๊ฐ•ํ™”
โ€ข
์ž๋™ ๊ณ„์‚ฐ ์ฒ˜๋ฆฌ
โ€ข
๋ทฐ์—์„œ DML ์ง€์›(INSTEAD OF)