暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

数据库对象管理Oracle模式创建组合触发器

2023-04-09
200

一个组合触发器中可以包含行前、行后、语句前、语句后四种触发事件中的一到四种或只包含一个 INSTEAD OF 触发事件。

概述

在表或可编辑视图上创建的组合触发器可以在多个时间点触发。组合触发器按照每个时间点分隔成段,每个时间段都有自己的可执行部分和异常处理部分(可选的),所有这些部分都可以访问一个通用的 PL 状态。通用状态在触发语句开始时建立,在触发语句完成时销毁,即使触发语句导致错误时也不会受到影响。

组合触发器的使用限制

组合 DML 触发器具有以下限制:

  • OLDNEW 和 PARENT 不能出现在声明部分,也不能出现在BEFORE STATEMENT 部分或 AFTER STATEMENT 部分。

  • 只有 BEFORE EACH ROW 部分可以更改 NEW 的值。

  • 一个时间段不能处理在另一个时间段中引发的异常。

  • 如果一个时间段内包含 GOTO 语句,则 GOTO 语句的目标必须在同一时间段中。

创建触发器

在组合触发器的声明部分(可选),您可以声明其所有由时间点分隔的段都能使用的变量和子程序。当触发器被触发时,声明部分在任何时间段运行之前运行。变量和子程序在触发语句所作用的时间段内存在。

在不可编辑视图上创建的组合触发器并不是真正意义上的组合触发器,因为它只有一个时间段。在不可编辑视图上创建一个简单的组合触发器的语法如下:

CREATE TRIGGER FOR dml_event_clause ON view_name
COMPOUND TRIGGER
INSTEAD OF EACH ROW IS BEGIN
  sql_statement;
END INSTEAD OF EACH ROW;
复制

在表或可编辑视图上创建的组合触发器至少有一个时间段。如果触发器有多个时间段,它们可以按任意顺序排列,但时间点不能重复。如果时间段不存在,则在该时间点不会发生任何操作。

组合触发器主要有如下几个时间段:

时间节点时间段
在触发语句运行之前BEFORE STATEMENT
在触发语句运行后AFTER STATEMENT
在触发语句影响的每一行之前BEFORE EACH ROW
在触发语句影响的每一行之后AFTER EACH ROW

组合触发器没有初始化部分,但是 BEFORE STATEMENT 可以进行任何所需的初始化,因为它先于其他时间段运行。

如果组合触发器既没有 BEFORE STATEMENT 部分,也没有 AFTER STATEMENT 部分,并且其触发语句不影响任何行,则触发器永远不会触发。

使用组合触发器将更改信息记录到一个表的子表中。示例如下:

  CREATE TABLE emp_salaries (
      emp_id NUMBER NOT NULL,
      change_date DATE   NOT NULL,
      salary NUMBER(8,2) NOT NULL,
      FOREIGN KEY (emp_id)
      REFERENCES employees (employee_id) ON DELETE CASCADE);
      
  obclient> delimiter /
  
  obclient> CREATE OR REPLACE TRIGGER maintain_emp_salaries
  FOR UPDATE OF salary ON employees
    COMPOUND TRIGGER

  threshold CONSTANT SIMPLE_INTEGER := 7;

  TYPE salaries_t IS TABLE OF emp_salaries%ROWTYPE INDEX BY SIMPLE_INTEGER;
  sal  salaries_t;
  idx  SIMPLE_INTEGER := 0;

  PROCEDURE flush_proc IS
    n CONSTANT SIMPLE_INTEGER := sal.count();
  BEGIN
    FORALL j IN 1..n
      INSERT INTO emp_salaries VALUES sal(j);
    sal.delete();
    idx := 0;
    DBMS_OUTPUT.PUT_LINE('刷新' || n || '行');
  END flush_proc;

  -- AFTER EACH ROW 时间段:

  AFTER EACH ROW IS
  BEGIN
    idx := idx + 1;
    sal(idx).emp_id := :NEW.employee_id;
    sal(idx).change_date := SYSTIMESTAMP;
    sal(idx).salary := :NEW.salary;
    IF idx >= threshold THEN
      flush_proc();
    END IF;
  END AFTER EACH ROW;

  -- AFTER STATEMENT 时间段:

  AFTER STATEMENT IS
  BEGIN
    flush_proc();
  END AFTER STATEMENT;
END maintain_emp_salaries;
/
Query OK, 0 rows affected

obclient> SET SERVEROUTPUT ON;
Query OK, 0 rows affected


/* 将部门 50 中每个员工的工资提高 8% */
obclient> UPDATE employees
       SET salary = salary * 0.8
       WHERE department_id = 50
      /
Query OK, 10 rows affected 
Rows matched: 10  Changed: 10  Warnings: 0

刷新7行
Flushed 7 rows
刷新3行
Flushed 3 rows


/* 等待 3 秒钟 */
obclient> BEGIN
      DBMS_LOCK.SLEEP(3);
      END
     /
Query OK, 1 row affected(3.01 sec)


/* 将部门 50 中每个员工的工资提高 1.1% */
obclient> UPDATE employees
       SET salary = salary * 1.1
       WHERE department_id = 50
     /
Query OK, 10 rows affected 
Rows matched: 10  Changed: 10  Warnings: 0

刷新7行
Flushed 7 rows
刷新3行
Flushed 3 rows


/* 查看 emp_salaries 表中的员工表的更改情况 */
obclient> SELECT emp_id, count(*) num
       FROM emp_salaries
       GROUP BY emp_id
     /
+--------+------+
| EMP_ID | NUM  |
+--------+------+
|    120 |    1 |
|    121 |    1 |
|    122 |    1 |
|    123 |    1 |
|    124 |    1 |
|    125 |    1 |
|    126 |    1 |
|    127 |    1 |
|    128 |    1 |
|    129 |    1 |
+--------+------+
10 rows in set
复制
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论