PL/SQL触发器

文章目录

  • 触发器的概述
  • 一、触发器
      • 1.语法:
          • 语法说明
      • 2、对触发器的禁用启用删除操作
      • 3、练习
  • 总结


触发器的概述

提示:触发器简来说就隐式被执行的过程

分类:

  1. dml触发器
  2. 系统触发器
  3. ddl触发器

一、触发器

1.语法:

CREATE [OR REPLACE] TRIGGER TRIGGER_NAME
{BEFORE|AFTER}
{INSERT|DELETE|UPDATE|[OF COLUMN [,COLUMN..,]]}
OR {INSERT|DELETE|UPDATE|[OF COLUMN [,COLUMN..,]]}
ON [SCHEMA.] TABLE_NAME|[SCHEMA.]VIEW_NAME
[FOR EACH ROW]
[WHEN CONDITION]
BEGIN
TRIGGER_BODY;
END;
语法说明
  • or replace 覆盖
  • before 在触发之前执行
  • after在触发事件之后执行
  • insert/delete/update 在插入、删除、更新操作时触发
  • or 可以多个操作同时定义触发器
  • on 对哪一个表或视图进行监视
  • for each row 带上也就是记录触发器(行级触发器)比如删除一条执行触发器一次
  • when condition 条件表达式
    代码实现:在员工表插入数据前打印helloworld
--在员工表插入记录前 打印hello world

CREATE TRIGGER  MY_TRIGGER1
BEFORE
INSERT --在插入数据时执行触发器
ON SCOTT.EMP --对scott下的emp表进行监视
FOR EACH ROW --行级触发器
  BEGIN
    DBMS_OUTPUT.PUT_LINE('HELLOWROLD');
  END;

--测试触发器
INSERT INTO EMP(EMPNO,ENAME) VALUES(2569,'HAHA');
COMMIT;

代码实现二:为了禁止工作人员在周四周五改变员工信息,(before改变前执行触发器)

-- 为了禁止工作人员在周四周五改变员工信息,(before改变前执行触发器)
CREATE TRIGGER MY_TRIGGER2
BEFORE
INSERT OR UPDATE OR DELETE
ON SCOTT.EMP
FOR EACH ROW --行级触发器
  BEGIN
    IF TO_CHAR(SYSDATE,'DAY') IN ('星期四','星期五') THEN
      --抛出异常 ,提醒不能更新员工信息
      RAISE_APPLICATION_ERROR(-1000,'对不起,休息日不能更改员工信息');
    END IF;
  END;
  
SELECT to_char(SYSDATE,'DAY') FROM dual;
DELETE FROM EMP WHERE EMPNO=2569;
SELECT * FROM EMP;

代码实现三:对所有员工薪资加1000,并打印旧的薪资和新的薪资

CREATE TRIGGER MY_TRIGGER3
AFTER
UPDATE 
ON SCOTT.EMP
FOR EACH ROW --行级触发器
  BEGIN
    DBMS_OUTPUT.PUT_LINE('HELLOWORLD');
  END;

UPDATE EMP SET SAL=SAL+1000;
-- 对所有员工薪资加1000,并打印旧的薪资和新的薪资
CREATE OR REPLACE TRIGGER MY_TRIGGER3
AFTER
UPDATE ON SCOTT.EMP
FOR EACH ROW --行级触发器
  BEGIN
  --返回旧的薪资:old.sal,新new.sal    DBMS_OUTPUT.PUT_LINE('OLD SAL:'||:OLD.SAL||',NEW SAL:'||:NEW.SAL);
  END;

UPDATE EMP SET SAL=SAL+1000;
COMMIT;

PL/SQL触发器_第1张图片

代码实现四:创建emp备份表(my_emp),编写触发器,在对my_emp数据进行删除的时,在my_emp_bak备份表插入my_emp删除的数据

-- 创建emp备份表(my_emp),编写触发器,在对my_emp数据进行删除的时,在my_emp_bak备份表插入my_emp删除的数据
--创建my_emp表
CREATE TABLE MY_EMP
AS
SELECT  EMPNO,ENAME,SAL 
FROM EMP;
--查询my_emp表
SELECT * FROM MY_EMP;
--创建my_emp_bak表结构
CREATE TABLE MY_EMP_BAK
AS
SELECT * FROM MY_EMP
WHERE 1=0;
-- 查寻my_emp_bak表
SELECT * FROM MY_EMP_BAK;

SELECT * FROM EMP_1;
DELETE FROM EMP;
COMMIT;

SELECT * FROM EMP;
CREATE TABLE EMP AS SELECT * FROM EMP_1;

CREATE OR REPLACE TRIGGER MY_TRIGGER4
BEFORE
DELETE
ON SCOTT.MY_EMP
FOR EACH ROW --行级触发器
  BEGIN
    INSERT INTO MY_EMP_BAK VALUES(:OLD.EMPNO,:OLD.ENAME,:OLD.SAL);
    --DML 操作无需commit
  END;
--测试触发器
DELETE FROM MY_EMP;
COMMIT;
SELECT * FROM MY_EMP;
SELECT * FROM MY_EMP_BAK;

2、对触发器的禁用启用删除操作

--禁用触发器
ALTER TRIGGER MY_TRIGGER4 DISABLE;
--启用触发器
ALTER TRIGGER MY_TRIGGER4 ENABLE;
--删除触发器
DROP TRIGGER MY_TRIGGER4;

3、练习

代码实现一:创建触发器,它将映射emp表中每个部门的总人数和总工资

--创建触发器,它将映射emp表中每个部门的总人数和总工资

--首先创建映射表(表的备份将查询结果作为表内容)
CREATE TABLE DEPT_SAL 
AS
SELECT DEPTNO,COUNT(EMPNO) TOTAL_EMP,SUM(SAL) TOTAL_SAL
FROM EMP
GROUP BY DEPTNO;
--查看映射表
SELECT * FROM DEPT_SAL;

-- 创建触发器
CREATE OR REPLACE TRIGGER MY_TRIGGER5
AFTER
INSERT OR UPDATE OR DELETE
ON EMP
DECLARE
  CURSOR CUR_EMP 
  IS
  SELECT DEPTNO,COUNT(EMPNO) TOTAL_EMP,SUM(SAL) TOTAL_SAL 
  FROM EMP 
  GROUP BY DEPTNO;
BEGIN
  DELETE DEPT_SAL;
  FOR V_EMP IN CUR_EMP LOOP
    DBMS_OUTPUT.PUT_LINE(v_emp.deptno || v_emp.total_emp || v_emp.total_sal);
    INSERT INTO DEPT_SAL VALUES(V_EMP.DEPTNO,V_EMP.TOTAL_EMP,V_EMP.TOTAL_SAL);
  END LOOP;
END;
-- 对emp表进行DML操作
SELECT * FROM EMP;
INSERT INTO emp(empno,deptno,sal) VALUES(123,10,10000);
SELECT * FROM dept_sal;
DELETE EMP WHERE empno=123;
SELECT * FROM dept_sal;

代码实现二:创建触发器,用来记录表删除数据

--创建触发器,用来记录表的删除数据

-- 创建表
CREATE TABLE EMPLOYEE(
  ID VARCHAR2(4) NOT NULL,
  NAME VARCHAR2(15) NOT NULL,
  AGE NUMBER(2) NOT NULL,
  SEX CHAR NOT NULL
);
--插入数据
INSERT INTO EMPLOYEE 
VALUES('E101','ZHAO',23,'M');
INSERT INTO EMPLOYEE 
VALUES('E102','JIAN',21,'F');
COMMIT;
--创建记录表
CREATE TABLE OLD_EMP AS SELECT * FROM EMPLOYEE WHERE 1=0;
--查看表数据
SELECT * FROM EMPLOYEE;
SELECT * FROM OLD_EMP;
-- 创建触发器
CREATE OR REPLACE TRIGGER MY_TIRGGER6
AFTER 
DELETE
ON EMPLOYEE
FOR EACH ROW
BEGIN 
  INSERT INTO OLD_EMP 
  VALUES(:OLD.ID,:OLD.NAME,:OLD.AGE,:OLD.SEX);
END;
--测试代码
DELETE FROM EMPLOYEE;
SELECT * FROM EMPLOYEE;
SELECT * FROM OLD_EMP;

代码实现三:创建触发器,利用视图插入数据

--创建触发器利用视图插入数据

--创建表
CREATE TABLE tab1 (tid NUMBER(4) PRIMARY KEY,tname VARCHAR2(20),tage NUMBER(2));
CREATE TABLE tab2 (tid NUMBER(4),ttel VARCHAR2(15),tadr VARCHAR2(30));

--插入数据
INSERT INTO tab1 VALUES(101,'zhao',22);
INSERT INTO tab1 VALUES(102,'yang',20);
INSERT INTO tab2 VALUES(101,'13761512841','AnHuiSuZhou');
INSERT INTO tab2 VALUES(102,'13563258514','AnHuiSuZhou');
COMMIT;
--创建视图连接两张表
CREATE OR REPLACE VIEW tab_view AS SELECT tab1.tid,tname,ttel,tadr FROM tab1,tab2 WHERE tab1.tid = tab2.tid;

--创建触发器
CREATE OR REPLACE TRIGGER MY_TRIGGER7
INSTEAD OF 
INSERT 
ON TAB_VIEW
BEGIN
  INSERT INTO TAB1(TID,TNAME) VALUES(:NEW.TID,:NEW.TNAME);
  INSERT INTO TAB2(TTEL,TADR) VALUES(:NEW.TTEL,:NEW.TADR);
  
END;

--测试数据
INSERT INTO tab_view VALUES(106,'ljq','13886681288','beijing');
--查询
SELECT * FROM tab_view;
SELECT * FROM tab1;
SELECT * FROM tab2;

代码实现:创建触发器,比较emp表更新的薪资

--创建触发器 比较emp中更新的工资

CREATE OR REPLACE TRIGGER MY_TRIGGER8
BEFORE
UPDATE
ON EMP
FOR EACH ROW
BEGIN 
  IF :OLD.SAL>:NEW.SAL THEN
    DBMS_OUTPUT.PUT_LINE('工资减少了');
  ELSIF :OLD.SAL<:NEW.SAL THEN
    DBMS_OUTPUT.PUT_LINE('工资增加了');
  ELSE
    DBMS_OUTPUT.PUT_LINE('工资没变');
  END IF;
  DBMS_OUTPUT.PUT_LINE('OLDSAL'||:OLD.SAL);
  DBMS_OUTPUT.PUT_LINE('NEW.SAL'||:NEW.SAL);
END;

--测试

UPDATE EMP SET SAL=8000 WHERE EMPNO='7369';
COMMIT;

代码实现:创建触发器,将create、drop 存储在log_info中

--创建触发器 ,将操作create 、drop存储在log_info 表中

CREATE TABLE log_info(
  manager_user VARCHAR2(15),
  manager_date VARCHAR2(15),
  manager_type VARCHAR2(15),
  obj_name   VARCHAR2(15),
  obj_type   VARCHAR2(15)
);

-- 创建触发器

CREATE OR REPLACE TRIGGER MY_TRIGGER8
AFTER
CREATE OR DROP
ON SCHEMA
BEGIN
  INSERT INTO LOG_INFO
  VALUES(USER,SYSDATE,SYS.DICTIONARY_OBJ_NAME,SYS.DICTIONARY_OBJ_OWNER,SYS.DICTIONARY_OBJ_TYPE);
END;

--测试
CREATE TABLE A(ID NUMBER);
CREATE TYPE AA AS OBJECT(ID NUMBER);
DROP TABLE A;
DROP TYPE AA;

--看效果
SELECT * FROM LOG_INFO;
--相关数据字典
SELECT * FROM USER_TRIGGERS;

注意

  • Oracle数据库触发器不能调用commit,rollback 等dcl语句
  • 在触发器中不能运行ddl语句和commit,rollback语句
  • ddl语句:DDL语句⽤语定义和管理数据库中的对象,如Create,Alter,Drop,truncate等;DDL操作是隐性提交的!
    触发器使用ddl语句

-在可以在触发器中加⼊:pragma autonomous_transaction;(在DECLARE后⾯) 表⽰是⾃由事务处理。

CREATE OR REPLACE TRIGGER T_create BEFORE insert ON T_Tax_INS_BD
for each row
DECLARE
pragma autonomous_transaction; 
NRDSId varchar(500):=''; 
begin 

⼀: 在触发器中使⽤DDL语句。 如 drop table t1 ,触发的时候会报错,
ORA-04092: cannot COMMIT in a trigger,因为DDL语句隐含commit。
⼆: 触发器的主体中使⽤了commit或rollback等DCL语句。ORA-04092:
cannot COMMIT in a trigger , 因为DML(delete/update/insert)触发器中
不能使⽤DDL(CREATE,DROP,ALTER)语句,也不能使⽤事务控制语句(DCL)
(ROLLBACK, COMMIT,SAVEPOINT)。特别注意的是,在触发器的主体中引⽤的
函数(function)/过程(procedure)中也不能有事物控制语句。
三: 注意: 系统级触发器(System Triggers)中可以使⽤DDL语句。
四: 处理⽅法: 去掉事务控制语句;如果procedure中必须有commit,那么可以
将commit 拿掉,由外部控制 。


总结

没有自治事务,在trigger就不能写commit

了解自治事务:转载

你可能感兴趣的:(数据库,sql,数据库)