日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Oracle入门(十四.21)之创建DML触发器:第二部分

發布時間:2023/12/3 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Oracle入门(十四.21)之创建DML触发器:第二部分 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、使用條件謂詞

在上文中,看到了一個觸發器,可以防止在周末插入EMPLOYEES:

CREATE OR REPLACE TRIGGER secure_emp BEFORE INSERT ON employees BEGINIF TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN') THENRAISE_APPLICATION_ERROR(-20500,'You may insert into EMPLOYEES'||' table only during business hours');END IF; END;

假設希望在周末期間阻止EMPLOYEES上的任何DML操作,并為INSERT,UPDATE和DELETE提供不同的錯誤消息。 可以創建三個單獨的觸發器; 不過,也可以用一個觸發器來做到這一點。

CREATE OR REPLACE TRIGGER secure_emp BEFORE INSERT OR UPDATE OR DELETE ON employees BEGINIF TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN') THENIF DELETING THEN RAISE_APPLICATION_ERROR(-20501,'You may delete from EMPLOYEES'||' table only during business hours');ELSIF INSERTING THEN RAISE_APPLICATION_ERROR(-20502,'You may insert into EMPLOYEES'||' table only during business hours');ELSIF UPDATING THEN RAISE_APPLICATION_ERROR(-20503,'You may update EMPLOYEES'||' table only during business hours');END IF; END IF; END;

可以使用條件謂詞來測試特定列上的UPDATE:

CREATE OR REPLACE TRIGGER secure_emp BEFORE UPDATE ON employees BEGINIF UPDATING('SALARY') THENIF TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN')THEN RAISE_APPLICATION_ERROR(-20501,'You may update SALARY'||' only during business hours');END IF;ELSIF UPDATING('JOB_ID') THENIF TO_CHAR(SYSDATE,'DY') = 'SUN'THEN RAISE_APPLICATION_ERROR(-20502,'You may not update JOB_ID on Sunday');END IF; END IF; END;


二、了解行觸發器

請記住,對于每個觸發的DML語句,語句觸發器只執行一次:

CREATE OR REPLACE TRIGGER log_emps AFTER UPDATE OF salary ON employees BEGIN INSERT INTO log_emp_table (who, when)VALUES (USER, SYSDATE); END;無論觸發語句是更新一個員工,幾個員工,還是根本沒有員工,該觸發器都會在日志表中正好插入一行。

假設您想為每個更新的員工在日志表中插入一行。 例如,如果更新了四名員工,則將四行插入日志表中。 你需要一個行觸發器。

(1)行觸發器觸發序列

對于受觸發DML語句影響的每一行,行觸發器都會觸發(執行)一次,無論是在處理該行之前還是僅在AFTER之后。 如果五個員工在部門50中,則行觸發器執行五次:

UPDATE employeesSET salary = salary * 1.1WHERE department_id = 50;

(2)創建行觸發器

CREATE OR REPLACE TRIGGER log_emps AFTER UPDATE OF salary ON employees FOR EACH ROW BEGIN INSERT INTO log_emp_table (who, when)VALUES (USER, SYSDATE); END;

????可以使用FOR EACH ROW來指定行觸發器。 上一張幻燈片中的UPDATE語句現在將五行插入日志表中,每個EMPLOYEE行更新一行。但是,日志表中的所有五行都是相同的。 日志表不顯示哪些員工已更新,或者他們的薪水有哪些變化。

(3)使用:OLD和:NEW限定符

????只有在行觸發器中,您是否可以在當前正在更新的EMPLOYEES行中引用和使用舊列和新列值。
????代碼:OLD.column_name引用preupdate值,以及:NEW.column_name引用更新后的值。

例如,如果UPDATE語句將雇員的工資從10000更改為11000,則:OLD.salary的值為10000,以及:NEW.salary的值為11000.現可以將所需的數據插入日志記錄表。

要記錄employee_id,無論是否編碼:OLD.employee_id或:NEW.employee_id,都無關緊要嗎?有區別嗎?

CREATE OR REPLACE TRIGGER log_emps AFTER UPDATE OF salary ON employees FOR EACH ROW BEGIN INSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.salary, :NEW.salary); END;

行觸發器的第二個例子

CREATE OR REPLACE TRIGGER audit_emp_values AFTER DELETE OR INSERT OR UPDATE ON employees FOR EACH ROW BEGININSERT INTO audit_emp(user_name, time_stamp, id,old_last_name, new_last_name, old_title,new_title, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.last_name, :NEW.last_name, :OLD.job_id,:NEW.job_id, :OLD.salary, :NEW.salary); END;

第二個示例:測試audit_emp_values觸發器

INSERT INTO employees (employee_id, last_name, job_id, salary, ...)VALUES (999, 'Temp emp', 'SA_REP', 1000,...);UPDATE employees SET salary = 2000, last_name = 'Smith' WHERE employee_id = 999; SELECT user_name, time_stamp, ...FROM audit_emp;

行觸發器的第三個例子

假設你需要防止不是總裁或副總裁的雇員的工資超過15000美元。

CREATE OR REPLACE TRIGGER restrict_salary BEFORE INSERT OR UPDATE OF salary ON employees FOR EACH ROW BEGINIF NOT (:NEW.job_id IN ('AD_PRES', 'AD_VP'))AND :NEW.salary > 15000 THENRAISE_APPLICATION_ERROR (-20202,'Employee cannot earn more than $15,000.');END IF; END;

測試restrict_salary觸發器:

UPDATE employees SET salary = 15500WHERE last_name IN ('King','Davies'); King是(副)主席,但Davies不是。 此UPDATE語句會產生以下錯誤:
ORA-20202: Employee cannot earn more than $15,000. ORA-06512: at “USVA_TEST_SQL01_T01.RESTRICT_SALARY”, line 4 ORA-04088: error during execution of trigger ‘USVA_TEST_SQL01_T01.RESTRICT_SALARY’ 2. WHERE last_name IN (‘King’, ‘Davies’);

EMPLOYEES行都不會更新,因為UPDATE語句必須完全成功或根本不成功。


第四個例子:用觸發器實現完整性約束

EMPLOYEES表在DEPARTMENTS表的DEPARTMENT_ID列中具有外鍵約束。 DEPARTMENT_ID 999不存在,因此此DML語句違反了約束條件,員工行未更新:

UPDATE employees SET department_id = 999 WHERE employee_id = 124;

可以使用觸發器自動創建新部門。?


第四個例子:創建觸發器:

CREATE OR REPLACE TRIGGER employee_dept_fk_trg BEFORE UPDATE OF department_id ON employees FOR EACH ROW DECLAREv_dept_id departments.department_id%TYPE; BEGINSELECT department_id INTO v_dept_id FROM departmentsWHERE department_id = :NEW.department_id; EXCEPTIONWHEN NO_DATA_FOUND THENINSERT INTO departments VALUES(:NEW.department_id,'Dept '||:NEW.department_id, NULL, NULL);

來測試它:

UPDATE employees SET department_id = 999 WHERE employee_id = 124; -- Successful after trigger is fired

三、使用REFERENCING子句

再看一下行觸發器的第一個例子:
CREATE OR REPLACE TRIGGER log_emps AFTER UPDATE OF salary ON employees FOR EACH ROW BEGININSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.salary, :NEW.salary); END;

如果EMPLOYEES表的名稱不同,該怎么辦?
如果它被稱為OLD呢? OLD不是一個好名字,但是可能的。 我們的代碼現在會是什么樣子?

OLD現在意味著兩件事:它是一個值限定符(如:NEW),也是一個表名。 該代碼將起作用,但會令人困惑。 我們不需要

使用:OLD和:NEW。 我們可以通過包含REFERENCING子句來使用不同的限定符。

CREATE OR REPLACE TRIGGER log_emps AFTER UPDATE OF salary ON old REFERENCING OLD AS former NEW AS latter FOR EACH ROW BEGININSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :former.employee_id,:former.salary, :latter.salary); END;

FORMER和LATTER被稱為關聯名稱。 他們是OLD和NEW的別名。 我們可以選擇任何我們喜歡的相關名稱(例如TOM和MARY),只要它們不是保留字。 REFERENCING子句只能用于行觸發器。


四、使用WHEN子句

看看這個觸發代碼。 只有在新薪水高于舊薪水時才會記錄薪資變化。
CREATE OR REPLACE TRIGGER restrict_salary AFTER UPDATE OF salary ON employees FOR EACH ROW BEGINIF :NEW.salary > :OLD.salary THENINSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.salary, :NEW.salary);END IF; END;整個觸發器主體是一個單一的IF語句。 在現實生活中,這可能是許多代碼行,包括CASE語句,循環和許多其他構造。 這將很難閱讀。

可以在觸發器標題中編寫我們的IF條件,就在BEGIN子句之前。

CREATE OR REPLACE TRIGGER restrict_salary AFTER UPDATE OF salary ON employees FOR EACH ROW WHEN (NEW.salary > OLD.salary) BEGININSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.salary, :NEW.salary); END;

這段代碼更容易閱讀,特別是如果觸發器體長且復雜。 WHEN子句只能用于行觸發器。


五、INSTEAD OF觸發器

復雜視圖(例如基于聯接的視圖)無法更新。 假設EMP_DETAILS視圖是基于EMPLOYEES和DEPARTMENTS聯合的復雜視圖。 以下SQL語句失敗:
INSERT INTO emp_detailsVALUES (9001,'ABBOTT',3000, 10, 'Administration');

可以通過創建一個觸發器來直接更新兩個基表,而不是嘗試(和失敗)更新視圖。

INSTEAD OF觸發器總是行觸發器。


(1)一個INSTEAD OF觸發器的例子

將INSERT執行到基于NEW_EMPS和NEW_DEPTS表的EMP_DETAILS視圖中:

INSERT INTO emp_detailsVALUES (9001,'ABBOTT',3000, 10, 'Administration');

(2)創建一個INSTEAD OF觸發器

步驟1:創建表格和復雜視圖:

CREATE TABLE new_emps AS SELECT employee_id,last_name,salary,department_idFROM employees; CREATE TABLE new_depts AS SELECT d.department_id,d.department_name,sum(e.salary) dept_salFROM employees e, departments dWHERE e.department_id = d.department_idGROUP BY d.department_id,d.department_name; CREATE VIEW emp_details AS SELECT e.employee_id, e.last_name, e.salary,e.department_id, d.department_nameFROM new_emps e, new_depts dWHERE e.department_id = d.department_id;

第2步:創建INSTEAD OF觸發器:

CREATE OR REPLACE TRIGGER new_emp_dept INSTEAD OF INSERT ON emp_details BEGININSERT INTO new_empsVALUES (:NEW.employee_id, :NEW.last_name,:NEW.salary, :NEW.department_id);UPDATE new_deptsSET dept_sal = dept_sal + :NEW.salaryWHERE department_id = :NEW.department_id; END;

(3)行觸發器重訪

看看這個行觸發器,記錄員工的工資變化:

CREATE OR REPLACE TRIGGER log_emps AFTER UPDATE OF salary ON employees FOR EACH ROW BEGIN INSERT INTO log_table(employee_id, change_date, salary)VALUES (:OLD.employee_id, SYSDATE, :NEW.salary); END;

如果有一百萬名員工,并且你給每個員工5%的工資增長:

UPDATE employees SET salary = salary * 1.05;

行觸發器將自動執行一百萬次,每次插入一行。 這將非常緩慢。


在課程的前期,學習了如何使用批量綁定(FORALL)來加速DML。 我們可以在我們的觸發器中使用FORALL嗎?

CREATE OR REPLACE TRIGGER log_emps AFTER UPDATE OF salary ON employees FOR EACH ROW DECLARETYPE t_log_emp IS TABLE OF log_table%ROWTYPEINDEX BY BINARY_INTEGER;log_emp_tab t_log_emp; BEGIN... Populate log_emp_tab with employees’ change data FORALL i IN log_emp_tab.FIRST..log_emp_tab.LASTINSERT INTO log_table VALUES log_emp_tab(i); END;

這不起作用。 為什么不? 提示:請記住,這是一個行觸發器,并考慮LOG_EMP_TAB收集變量的作用域。

CREATE OR REPLACE TRIGGER log_emps AFTER UPDATE OF salary ON employees FOR EACH ROW DECLARETYPE t_log_emp IS TABLE OF log_table%ROWTYPEINDEX BY BINARY_INTEGER;log_emp_tab t_log_emp; BEGIN... Populate log_emp_tab with employees’ change data FORALL i IN log_emp_tab.FIRST..log_emp_tab.LASTINSERT INTO log_table VALUES log_emp_tab(i); END;

在觸發器的每次執行結束時,觸發器變量會丟失范圍。 所以每次觸發行觸發器時,LOG_EMP_TAB中已收集的所有數據都將丟失。為了避免丟失這些數據,我們需要一個只觸發一次的觸發器 - 一個語句觸發器。 但要引用每行的列值(使用:OLD和:NEW),我們需要一個行觸發器。

但是單個觸發器不能同時是行觸發器和語句觸發器。 對? 錯誤! 我們創建一個復合觸發器。


六、什么是復合觸發器?

一個觸發器,可以包含針對每個可能的時間點的操作:觸發語句之前,每行之前,每行之后,觸發語句之后。 復合觸發器有一個聲明部分,以及每個時間點的部分。 你不必包含所有的時間點,只需要你需要的時間點。 復合觸發器變量的范圍是整個觸發器,因此它們在整個執行過程中保留其范圍。

(1)復合觸發結構


(2)例子

這個例子有一個聲明部分和四個可能的時間點部分中的兩個。


(3)完整代碼

CREATE OR REPLACE TRIGGER log_emps FOR UPDATE OF salary ON employees COMPOUND TRIGGER DECLARETYPE t_log_emp IS TABLE OF log_table%ROWTYPEINDEX BY BINARY_INTEGER;log_emp_tab t_log_emp;v_index BINARY_INTEGER := 0; AFTER EACH ROW IS BEGINv_index := v_index + 1;log_emp_tab(v_index).employee_id := :OLD.employee_id;log_emp_tab(v_index).change_date := SYSDATE;log_emp_tab(v_index).salary := :NEW.salary; END AFTER EACH ROW; AFTER STATEMENT IS BEGIN FORALL I IN log_emp_tab.FIRST..log_emp_tab.LASTINSERT INTO log_table VALUES log_emp_tab(i); END AFTER STATEMENT; END log_emps;




總結

以上是生活随笔為你收集整理的Oracle入门(十四.21)之创建DML触发器:第二部分的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。