Oracle數據庫數據編程SQL<3.7 PL/SQL 觸發器(Trigger)>

觸發器是Oracle數據庫中的一種特殊存儲過程,它會在特定數據庫事件發生時自動執行。觸發器通常用于實現復雜的業務規則、數據驗證、審計跟蹤等功能。

目錄

一、觸發器基本概念

1. 觸發器特點

2. 觸發器組成要素

二、觸發器類型

1. DML觸發器

2. DDL觸發器

3. 系統/數據庫事件觸發器

4. INSTEAD OF觸發器

三、創建DML觸發器

1. 基本語法

2. 行級觸發器示例

3. 語句級觸發器示例

四、特殊觸發器

1. 復合觸發器(11g+)

2. INSTEAD OF觸發器(用于視圖)

3. 系統事件觸發器

五、觸發器中的特殊變量和函數

1. OLD 和 NEW 偽記錄

2. 事件屬性函數

3. 條件謂詞

4. 簡單示例

5. 簡單練習

六、觸發器管理

1. 查看觸發器

2. 啟用/禁用觸發器

3. 重新編譯觸發器

4. 刪除觸發器

七、觸發器最佳實踐

八、常見問題解決方案

1. 避免觸發器遞歸

2. 處理大批量操作

3. 跨數據庫同步

九、觸發器使用注意事項


一、觸發器基本概念

觸發器在數據庫里以獨立的對象存儲,他與存儲過程不同的是,存儲過程通過其他程序來啟動運行或者直接運行,而觸發器是由一個事件來啟動運行。即觸發器是當某個事件發生時自動地隱式運行,并且觸發器不接收參數。?

1. 觸發器特點

  • 自動執行:滿足條件時由數據庫自動觸發

  • 事件驅動:響應特定DML或DDL操作

  • 無顯式調用:不能像存儲過程那樣直接調用

  • 事務感知:作為觸發語句的一部分執行

2. 觸發器組成要素

  • 觸發事件:INSERT、UPDATE、DELETE等DML操作

  • 觸發時機:BEFORE或AFTER

  • 觸發級別:行級(FOR EACH ROW)或語句級

  • 觸發條件:WHEN子句定義的條件

  • 觸發體:PL/SQL代碼塊

二、觸發器類型

1. DML觸發器

響應數據操作語言(DML)事件:

  • INSERT

  • UPDATE

  • DELETE

  • MERGE (10g+)

2. DDL觸發器

響應數據定義語言(DDL)事件:

  • CREATE

  • ALTER

  • DROP

  • TRUNCATE等

3. 系統/數據庫事件觸發器

響應數據庫系統事件:

  • 登錄/注銷(LOGON/LOGOFF)

  • 服務器錯誤(SERVERERROR)

  • 啟動/關閉(STARTUP/SHUTDOWN)

4. INSTEAD OF觸發器

用于視圖上的DML操作

三、創建DML觸發器

語句級觸發器(statement):當某觸發事件發生時,該觸發器執行一次

行級觸發器(row):當某觸發事件發生時,受到影響的每一行數據,觸發器都單獨執行一次

1. 基本語法

CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF}
{INSERT | UPDATE | DELETE [OR INSERT | UPDATE | DELETE...]}
ON {table_name | view_name}
[REFERENCING [OLD AS old] [NEW AS new]]
[FOR EACH ROW]
[WHEN (condition)]
[DECLAREdeclaration_statements]
BEGINexecutable_statements
[EXCEPTIONexception_handling_statements]
END [trigger_name];--化說明
create or replace trigger tri_name--創建觸發器
{before/after}--觸發的時間點{DML操作} on 表名
{for each row}--加上是行級觸發,不加是語句級
{when 條件}
begin
{referencing {OLD {as} old|new {as} NEW}new PARENT as parent}---觸發器的執行部分
end;
/-- 簡單示例1
create or replace trigger tr_1before delete on emp11
begininsert into emp12(ename)select ename from emp where empno = 7369;
end;
/
-- 簡單示例2
--創建一個觸發器 當刪除emp1表中的數據時,向emp2插入7788的員工信息
--并且刪除emp3中7788的信息。
-- 準備
create table emp1 as select * from emp;
create table emp2 as select * from emp;
create table emp3 as select * from emp;
-- 創建觸發器
create or replace trigger tr_1before delete on emp1
begininsert into emp2(ename, sal)select ename, sal from emp where empno = 7788;delete from emp3 where empno = 7788;
end;
/
-- 觸發
delete from emp1 where empno=7788;

2. 行級觸發器示例

-- 薪資變更審計觸發器
CREATE OR REPLACE TRIGGER audit_salary_change
BEFORE UPDATE OF salary ON employees
FOR EACH ROW
WHEN (NEW.salary <> OLD.salary)
DECLAREv_change_type VARCHAR2(10);
BEGIN-- 確定變更類型IF :NEW.salary > :OLD.salary THENv_change_type := 'RAISE';ELSEv_change_type := 'CUT';END IF;-- 記錄審計信息INSERT INTO salary_audit (audit_id, employee_id, old_salary, new_salary,change_type, change_date, changed_by) VALUES (audit_seq.NEXTVAL, :NEW.employee_id,:OLD.salary, :NEW.salary,v_change_type, SYSDATE, USER);
END audit_salary_change;
/

3. 語句級觸發器示例

-- 限制非工作時間操作
CREATE OR REPLACE TRIGGER restrict_after_hours_dml
BEFORE INSERT OR UPDATE OR DELETE ON employees
DECLAREv_current_time VARCHAR2(8) := TO_CHAR(SYSDATE, 'HH24:MI:SS');v_day_of_week VARCHAR2(3) := TO_CHAR(SYSDATE, 'DY');
BEGIN-- 工作日晚上8點到早上6點禁止操作IF v_day_of_week NOT IN ('SAT', 'SUN') AND (v_current_time > '20:00:00' OR v_current_time < '06:00:00') THENRAISE_APPLICATION_ERROR(-20001, '非工作時間(工作日6:00-20:00)不允許修改員工數據');END IF;
END restrict_after_hours_dml;
/

四、特殊觸發器

1. 復合觸發器(11g+)

CREATE OR REPLACE TRIGGER compound_emp_trigger
FOR INSERT OR UPDATE OR DELETE ON employees
COMPOUND TRIGGER-- 在觸發語句開始前執行BEFORE STATEMENT ISBEGINDBMS_OUTPUT.PUT_LINE('開始員工數據變更操作');END BEFORE STATEMENT;-- 每行變更前執行BEFORE EACH ROW ISBEGINDBMS_OUTPUT.PUT_LINE('準備變更員工ID: ' || NVL(:NEW.employee_id, :OLD.employee_id));END BEFORE EACH ROW;-- 每行變更后執行AFTER EACH ROW ISBEGINDBMS_OUTPUT.PUT_LINE('已完成變更員工ID: ' || NVL(:NEW.employee_id, :OLD.employee_id));END AFTER EACH ROW;-- 在觸發語句結束后執行AFTER STATEMENT ISBEGINDBMS_OUTPUT.PUT_LINE('員工數據變更操作完成');END AFTER STATEMENT;
END compound_emp_trigger;
/

2. INSTEAD OF觸發器(用于視圖)

-- 創建復雜視圖
CREATE OR REPLACE VIEW emp_dept_view AS
SELECT e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
FROM employees e JOIN departments d 
ON e.department_id = d.department_id;-- 創建INSTEAD OF觸發器支持插入
CREATE OR REPLACE TRIGGER io_emp_dept_insert
INSTEAD OF INSERT ON emp_dept_view
FOR EACH ROW
DECLAREv_dept_id NUMBER;
BEGIN-- 檢查部門是否存在BEGINSELECT department_id INTO v_dept_idFROM departmentsWHERE department_name = :NEW.department_name;EXCEPTIONWHEN NO_DATA_FOUND THENRAISE_APPLICATION_ERROR(-20001, '部門 ' || :NEW.department_name || ' 不存在');END;-- 插入員工記錄INSERT INTO employees (employee_id, last_name, salary, department_id) VALUES (employees_seq.NEXTVAL, :NEW.last_name, :NEW.salary, v_dept_id);
END io_emp_dept_insert;
/

3. 系統事件觸發器

-- 登錄審計觸發器
CREATE OR REPLACE TRIGGER logon_audit_trigger
AFTER LOGON ON DATABASE
BEGININSERT INTO logon_audit (audit_id, username, logon_time, host, ip_address) VALUES (logon_seq.NEXTVAL, USER,SYSDATE, SYS_CONTEXT('USERENV', 'HOST'),SYS_CONTEXT('USERENV', 'IP_ADDRESS'));
END logon_audit_trigger;
/-- DDL變更審計觸發器
CREATE OR REPLACE TRIGGER audit_ddl_changes
AFTER DDL ON SCHEMA
BEGININSERT INTO ddl_audit (audit_id, username, event_type, object_type,object_name, change_date) VALUES (ddl_audit_seq.NEXTVAL, USER,ORA_SYSEVENT, ORA_DICT_OBJ_TYPE,ORA_DICT_OBJ_NAME, SYSDATE);
END audit_ddl_changes;
/

五、觸發器中的特殊變量和函數

1. OLD 和 NEW 偽記錄

  • :OLD:引用操作前的行值(UPDATE/DELETE)---old.列名:此列中的舊值

  • :NEW:引用操作后的行值(INSERT/UPDATE)---new.列名:此列中的新值

2. 事件屬性函數

  • ORA_SYSEVENT:觸發事件的名稱

  • ORA_DICT_OBJ_TYPE:DDL操作的對象類型

  • ORA_DICT_OBJ_NAME:DDL操作的對象名稱

  • ORA_IS_ALTER_COLUMN:檢查是否修改了特定列

3. 條件謂詞

  • INSERTING:觸發器由INSERT觸發時為TRUE

  • UPDATING:觸發器由UPDATE觸發時為TRUE

  • DELETING:觸發器由DELETE觸發時為TRUE

條件謂語

old

new

update

delete

null

insert

null

4. 簡單示例

--創建一個觸發器
--當emp1被更新是,將更新前和更新后的ename和empno插入到emp2
--的對應列,在job列顯示更新前還是更新后,并且hiredate列插入更新時間
CREATE OR REPLACE TRIGGER TRE_1BEFORE UPDATE ON EMP1FOR EACH ROW
BEGININSERT INTO EMP2 (ENAME, EMPNO,job,hiredate) VALUES (:old.ename,:old.empno,'更新前',sysdate);INSERT INTO EMP2 (ENAME, EMPNO,job,hiredate) VALUES (:new.ename,:new.empno,'更新后',SYSDATE);
END;TRUNCATE TABLE emp2
UPDATE emp1 SET sal=sal+10000;
SELECT * FROM emp2
特性INSERTUPDATEDELETE
OLDNULL實際值實際值
NEW實際值實際值NULL

5. 簡單練習

--創建一張emp1和emp內容一樣。創建emp2只保留emp的格式。
--創建一個觸發器
--當emp1表被更新時觸發,將更新前和更新后的ename,empno插入到emp2中
-- 創建觸發器
create or replace trigger tr_3
before update on emp1
for each rowbegininsert into emp2 (ename,empno,job) values(:old.ename,:old.empno,'old');insert into emp2 (ename,empno,job) values(:new.ename,:new.empno,'new');
end;
/
-- 觸發
update emp1 set empno=empno-7000,ename=lower(ename);
-- 查詢驗證變化
select * from emp1;
select * from emp2;

六、觸發器管理

1. 查看觸發器

-- 查看觸發器定義
SELECT trigger_name, trigger_type, triggering_event, table_name, status
FROM user_triggers;-- 查看觸發器源代碼
SELECT text FROM user_source 
WHERE name = 'AUDIT_SALARY_CHANGE' AND type = 'TRIGGER'
ORDER BY line;

2. 啟用/禁用觸發器

-- 禁用單個觸發器
ALTER TRIGGER audit_salary_change DISABLE;-- 啟用單個觸發器
ALTER TRIGGER audit_salary_change ENABLE;-- 禁用表上的所有觸發器
ALTER TABLE employees DISABLE ALL TRIGGERS;-- 啟用表上的所有觸發器
ALTER TABLE employees ENABLE ALL TRIGGERS;

3. 重新編譯觸發器

ALTER TRIGGER trigger_name COMPILE;

4. 刪除觸發器

DROP TRIGGER trigger_name;

七、觸發器最佳實踐

  1. 保持簡潔:觸發器應簡短高效,避免復雜業務邏輯

  2. 避免遞歸:注意觸發器可能導致的級聯觸發

  3. 考慮性能:行級觸發器對大批量操作影響較大

  4. 明確文檔:記錄觸發器目的和業務規則

  5. 異常處理:妥善處理可能出現的錯誤

  6. 避免事務控制:通常不應在觸發器中提交或回滾

  7. 測試充分:驗證觸發器在各種場景下的行為

八、常見問題解決方案

1. 避免觸發器遞歸

CREATE OR REPLACE TRIGGER prevent_recursion
BEFORE UPDATE ON employees
FOR EACH ROW
DECLAREv_recursion_flag BOOLEAN := FALSE;
BEGIN-- 檢查是否由觸發器調用IF UPDATING AND DBMS_UTILITY.FORMAT_CALL_STACK LIKE '%PREVENT_RECURSION%' THENv_recursion_flag := TRUE;END IF;-- 如果不是遞歸調用,則執行業務邏輯IF NOT v_recursion_flag THEN-- 業務邏輯代碼END IF;
END;
/

2. 處理大批量操作

-- 使用BULK COLLECT和FORALL優化
CREATE OR REPLACE TRIGGER optimize_bulk_operation
AFTER INSERT ON large_table
DECLARETYPE id_array IS TABLE OF large_table.id%TYPE;v_ids id_array;
BEGIN-- 批量收集新插入的IDSELECT id BULK COLLECT INTO v_idsFROM large_tableWHERE status = 'NEW';-- 批量處理FORALL i IN 1..v_ids.COUNTUPDATE related_tableSET last_updated = SYSDATEWHERE large_table_id = v_ids(i);
END;
/

3. 跨數據庫同步

CREATE OR REPLACE TRIGGER sync_cross_database
AFTER INSERT OR UPDATE OR DELETE ON local_table
FOR EACH ROW
DECLAREPRAGMA AUTONOMOUS_TRANSACTION;
BEGINIF INSERTING THENINSERT INTO remote_table@remote_db VALUES (:NEW.id, :NEW.name);ELSIF UPDATING THENUPDATE remote_table@remote_dbSET name = :NEW.nameWHERE id = :OLD.id;ELSIF DELETING THENDELETE FROM remote_table@remote_db WHERE id = :OLD.id;END IF;COMMIT;
EXCEPTIONWHEN OTHERS THENROLLBACK;-- 記錄錯誤但不中斷主事務INSERT INTO error_log VALUES (SYSDATE, 'sync_cross_database', SQLERRM);COMMIT;
END;
/

九、觸發器使用注意事項

編寫觸發器時,需要注意一下幾點:

(1)觸發器不接受參數

(2)一個表上最多可有12個觸發器,但同一時間、同一事件、同一類型的觸發器只能有一個。并且各觸發器之間不能有矛盾。

(3)在一個表上的觸發器越多,對在該表上的DML操作的性能影響就越大

(4)觸發器最大為32KB。若確實需要,可以先建立過程,然后在觸發器中用CALL語句進行調用

(5)在觸發器的執行部分只能用DML語句(SELECT、INSERT、UDATE、DELETE),不能使用DDL語句(CREATE、ALTER、DROP)

(6)觸發器中不能包含事物控制語句(COMMIT、ROLLBACK、SAVEPOINT)。因為觸發器是觸發語句的一部分,觸發語句被提交、回退時,觸發器也被提交、回退了。

(7)在觸發器主體中調用的任何過程、函數,都不能使用事務控制語句

(8)在觸發器主體中不能聲明任何Long和Blob變量。新值new和舊值old也不能向表中的任何Long和Blob列。

(9)不同類型的觸發器(如DML觸發器、INSTEAD OF觸發器、系統觸發器)的語法格式和作用有比較大區別

觸發器是Oracle數據庫強大的功能,合理使用可以實現復雜的業務規則、數據完整性和審計需求。但也需謹慎使用,避免過度依賴觸發器導致系統難以維護。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/75319.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75319.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/75319.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2025年滲透測試面試題總結-某 攜程旅游-基礎安全工程師(題目+回答)

網絡安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 攜程旅游-基礎安全工程師 反序列化原理 核心原理 擴展分析 SQL注入本質 核心原理 擴展分析 SQL注…

CSS 邊框(Border)樣式詳解

CSS 邊框&#xff08;Border&#xff09;樣式詳解 CSS 提供了多種邊框樣式&#xff0c;使我們能夠控制元素的外觀。本文將詳細介紹 CSS 邊框的各種屬性及應用示例。 1. 基本邊框屬性 CSS 主要使用 border 相關屬性定義邊框&#xff0c;基本語法如下&#xff1a; border: [邊…

SpringCould微服務架構之Docker(6)

容器的基本命令&#xff1a; 1. docker exec &#xff1a;進入容器執行命令 2. docker logs: -f 持續查看容器的運行日志 3. docker ps&#xff1a;查看所有運行的容器和狀態 案例&#xff1a;創建運行一個容Nginx容器 docker run--name myNginx -p 80:80 -d nginx 命…

unity3d端監聽 uri scheme

一、消息監聽 1.創建一個腳本命名為 “URISchemeListener” &#xff0c;用于接收URI消息&#xff08;代碼如下&#xff09;。 using System; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.UI;public class URISchemeListener : MonoBehavio…

網絡信息安全應急演練方案

信息安全應急演練方案 總則 &#xff08;一&#xff09;編制目的 旨在建立并完善應對病毒入侵、Webshell 攻擊以及未授權訪問等信息安全突發事件的應急機制&#xff0c;提升組織對這類事件的快速響應、協同處理和恢復能力&#xff0c;最大程度降低事件對業務運營、數據安全和…

電商場景下高穩定性數據接口的選型與實踐

在電商系統開發中&#xff0c;API接口需要應對高并發請求、動態數據更新和復雜業務場景。我將重點解析電商場景對數據接口的特殊需求及選型方案。 一、電商API必備的四大核心能力 千萬級商品數據實時同步 支持SKU基礎信息/價格/庫存多維度更新每日增量數據抓取與歷史版本對比…

Android R adb remount 調用流程

目的&#xff1a;調查adb remount 與adb shell進去后執行remount的差異 調試方法&#xff1a;添加log編譯adbd,替換system\apex\com.android.adbd\bin\adbd 一、調查adb remount實現 關鍵代碼&#xff1a;system\core\adb\daemon\services.cpp unique_fd daemon_service_to…

多模態大語言模型arxiv論文略讀(二)

Identifying the Correlation Between Language Distance and Cross-Lingual Transfer in a Multilingual Representation Space ?? 論文標題&#xff1a;Identifying the Correlation Between Language Distance and Cross-Lingual Transfer in a Multilingual Representat…

【運維】負載均衡

老規矩&#xff0c;先占坑&#xff0c;后續更新。 開頭先理解一下所謂的“均衡”&#xff0c;不能狹義地理解為分配給所有實際服務器一樣多的工作量&#xff0c;因為多臺服務器的承載能力各不相同&#xff0c;這可能體現在硬件配置、網絡帶寬的差異&#xff0c;也可能因為某臺…

大型語言模型Claude的“思維模式”最近被公開解剖

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

Ubuntu環境安裝

1. 安裝gcc、g和make sudo apt update sudo apt install build-essential 2. 安裝cmake ubuntu安裝cmake的三種方法&#xff08;超方便&#xff01;&#xff09;-CSDN博客 3. 安裝ssh sudo apt-get install libssl-dev

【力扣hot100題】(028)刪除鏈表的倒數第N個節點

鏈表題還是太簡單了。 怕越界所以先定義了一個頭結點的頭結點&#xff0c;然后定義快慢指針&#xff0c;快指針先走n步&#xff0c;隨后一起走&#xff0c;直到快指針走到頭&#xff0c;刪除慢指針后一個節點即可。 /*** Definition for singly-linked list.* struct ListNod…

C/C++回調函數實現與std::function和std::bind介紹

1 概述 回調函數是一種編程模式&#xff0c;指的是將一個函數作為參數傳遞給另一個函數&#xff0c;并在某個特定事件發生時或滿足某些條件時由該函數調用。這種機制允許你定義在特定事件發生時應執行的代碼&#xff0c;從而實現更靈活和模塊化的程序設計。 2 傳統C/C回調實現…

【藍橋杯】單片機設計與開發,速成備賽

一、LED模塊開看&#xff0c;到大模板 二、刷第零講題目&#xff08;直接復制模板&#xff09; 三、空降芯片模板直接調用部分&#xff08;聽完再敲代碼&#xff09; 四、第十三講開刷省賽題&#xff08;開始自己背敲模板&#xff09; 五、考前串講刷一遍 b連接&#xff1…

Java 基礎-28- 多態 — 多態下的類型轉換問題

在 Java 中&#xff0c;多態&#xff08;Polymorphism&#xff09;是面向對象編程的核心概念之一。多態允許不同類型的對象通過相同的方法接口進行操作&#xff0c;而實際調用的行為取決于對象的實際類型。雖然多態提供了極大的靈活性&#xff0c;但在多態的使用過程中&#xf…

Epub轉PDF軟件Calibre電子書管理軟件

Epub轉PDF軟件&#xff1a;Calibre電子書管理軟件 https://download.csdn.net/download/hu5566798/90549599 一款好用的電子書管理軟件&#xff0c;可快速導入電腦里的電子書并進行管理&#xff0c;支持多種格式&#xff0c;閱讀起來非常方便。同時也有電子書格式轉換功能。 …

在 Ubuntu 22.04 上安裝 Docker Compose 的步驟

1. 確保已安裝 Docker Docker Compose 需要 Docker 作為依賴&#xff0c;請先安裝 Docker&#xff1a; sudo apt update sudo apt install docker.io sudo systemctl enable --now docker2. 下載 Docker Compose 二進制文件 推薦安裝最新穩定版的 Docker Compose&#xff08…

Mysql-數據庫、安裝、登錄

一. 數據庫 1. 數據庫&#xff1a;DataBase&#xff08;DB&#xff09;&#xff0c;是存儲和管理數據的倉庫。 2. 數據庫管理系統&#xff1a;DataBase Management System&#xff08;DBMS&#xff09;,操縱管理數據庫的大型軟件 3. SQL&#xff1a;Structured Query Language&…

基于SpringAOP面向切面編程的一些實踐(日志記錄、權限控制、統一異常處理)

前言 Spring框架中的AOP&#xff08;面向切面編程&#xff09; 通過上面的文章我們了解到了AOP面向切面編程的思想&#xff0c;接下來通過一些實踐&#xff0c;去更加深入的了解我們所學到的知識。 簡單回顧一下AOP的常見應用場景 日志記錄&#xff1a;記錄方法入參、返回值、執…

Rust 語言語法糖深度解析:優雅背后的編譯器魔法

之前介紹了語法糖的基本概念和在C/Python/JavaScript中的使用&#xff0c;今天和大家討論語法糖在Rust中的表現形式。 程序語言中的語法糖&#xff1a;讓代碼更優雅的甜味劑 引言&#xff1a;語法糖的本質與價值 語法糖(Syntactic Sugar) 是編程語言中那些并不引入新功能&…