【问题标题】:Is there any way we can emulate the way of using :old and :new of row triggers in statement triggers in PL/SQL有什么方法可以模拟在 PL/SQL 中的语句触发器中使用行触发器的 :old 和 :new 的方式
【发布时间】:2023-04-01 02:38:01
【问题描述】:

朋友们好日子!

所以,这是我的问题,上面写着 -

在 clientmstr 表上编写更新、删除触发器。系统 应跟踪正在更新的记录或 删除。更新或删除记录的旧值应该是 添加到 audit_trade 表中。 (使用两行单独实现 和语句触发器)

我的解决方案是这样的 -

-- For row trigger
create or replace trigger row_trigger
    before delete or update on client_master
    referencing old as old new as new
    for each row
begin
    insert into audit_table values(
    :old.client_id, :old.client_name, :old.client_budget
    );
end;
/

根据问题,我必须使用 statement trigger 来实现相同的功能,但我想不出可以做到的方法。我研究了语句触发器,我了解到我们不能在这里使用 :old 和 :new 。 有什么方法可以实现使用语句触发器添加审计表的相同行触发器方法吗?我刚开始学习PL/SQL才两天。我花了一整天时间在互联网上到处搜索 - 试图寻找一个例子,但我没有得到它。有人可以帮忙吗?


编辑

(1) 我正在使用 Oracle SQL Developer

(2) 正如 cmets 中有人建议的那样 - 在语句触发器中不可能做到这一点,我也这么认为。我必须在这个星期六提交我的作业。我和我的老师进行了一次对话——她说可以使用语句触发器来实现它。我问她怎么做——但她没有回答。然后我问她一个提示,她说这个(我复制粘贴她的文字)-

使用 col 作为操作和时间戳创建一个单独的表。插入更新和删除操作上的写入语句级触发器。触发器将通过在表中插入值来捕获触发的操作和时间戳。

我不明白这是什么意思或怎么做!谁能帮我解决这个问题?

【问题讨论】:

  • 您使用的是 MySQL 还是 Oracle?触发代码将是特定于产品的。
  • @jarlh 我正在使用 Oracle
  • @ShreyasChavhan,简短的回答,级别语句触发器不是为此而设计的。看看这里stackoverflow.com/questions/18741342/…
  • 你老师说的似乎没有道理。你想记录每行的信息,所以使用行触发器。语句触发器在这里有什么好处?我并不是说在语句触发器中记录更改行的信息是完全不可能的。我们可能可以在这里使用一些闪回查询,因此现在和几个小时(?)之前查看表,但到底是什么?

标签: sql oracle plsql triggers


【解决方案1】:

您可以使用复合触发器。

创建类型:

CREATE TYPE client_master_obj IS OBJECT(
  id     NUMBER,
  name   VARCHAR2(20),
  budget NUMBER(10,2)
);

CREATE TYPE client_master_table IS TABLE OF client_master_obj;

然后是触发器:

CREATE TRIGGER client_master_cmp_trigger
FOR DELETE OR UPDATE ON client_master
COMPOUND TRIGGER
  data client_master_table := client_master_table();
AFTER EACH ROW
  IS
  BEGIN
    data.EXTEND(1);
    data(data.COUNT) := client_master_obj(
                          :OLD.client_id,
                          :OLD.client_name,
                          :OLD.client_budget
                        );
  END AFTER EACH ROW;
AFTER STATEMENT
  IS
  BEGIN
    INSERT INTO audit_table (client_id, client_name, client_budget, trg_type)
    SELECT id,
           name,
           budget,
           'C'
    FROM   TABLE(data);
  END AFTER STATEMENT;
END;
/

其中,对于样本数据:

CREATE TABLE client_master (client_id, client_name, client_budget) AS
SELECT 1, 'Alice', 100 FROM DUAL UNION ALL
SELECT 2, 'Beryl', 200 FROM DUAL UNION ALL
SELECT 3, 'Carol', 300 FROM DUAL UNION ALL
SELECT 4, 'Debra', 400 FROM DUAL UNION ALL
SELECT 5, 'Emily', 500 FROM DUAL;

CREATE TABLE audit_table (client_id, client_name, client_budget, trg_type) AS
SELECT cm.*, 'X' FROM client_master cm WHERE 1 = 0;

然后:

UPDATE client_master
SET   client_budget = client_budget + 600
WHERE client_id IN (1, 2);

DELETE FROM client_master WHERE client_id IN (1, 3);

然后审计表包含(行触发器也为相同的更改触发):

SELECT * FROM audit_table;
CLIENT_ID CLIENT_NAME CLIENT_BUDGET TRG_TYPE
1 Alice 100 R
2 Beryl 200 R
1 Alice 100 C
2 Beryl 200 C
1 Alice 700 R
3 Carol 300 R
1 Alice 700 C
3 Carol 300 C

db小提琴here

【讨论】:

  • 是的,这是可能的。我喜欢复合触发器。但是,当我们可以立即将数据记录在一个行触发器中时,为什么会有人使用这种方法来处理给定的任务呢?
  • @ThorstenKettner,在同时更新数百万行的情况下,复合触发器的性能比行级触发器好得多
【解决方案2】:

使用复合触发器的方法相同,但虽然不是字面上的级别语句触发器,因为它们通常指的是表级触发器。

create or replace trigger row_compound_trigger
    for delete or update on client_master
    compound trigger 
    --
    -- an array structure to buffer all the row changes
    --
     type t_row_list is
       table of client_master%rowtype index by pls_integer;
 
     l_audit_rows      t_row_list;
 
     l_operation varchar2(1) :=
         case when updating then 'U'
              when deleting then 'D'
         end;
 
   before statement is
   begin
     --
     -- initialize the array
     --
     l_audit_rows.delete;
   end before statement;
 
   after each row is
   begin
     --
     -- at row level, capture all the changes into the array
     -- this variables use sys_context in case you want to use it ( not needed )
     --
     l_audit_rows(l_audit_rows.count+1).aud_who     := sys_context('USERENV','SESSION_USER');
     l_audit_rows(l_audit_rows.count).aud_when      := sysdate;
     l_audit_rows(l_audit_rows.count).aud_operation := l_operation;
     l_audit_rows(l_audit_rows.count).aud_module    := sys_context('USERENV','MODULE');
 
     if updating then
         l_audit_rows(l_audit_rows.count).client_id        := :new.client_id
         l_audit_rows(l_audit_rows.count).client_name      := :new.client_name
        ... all the fields 
     else
         l_audit_rows(l_audit_rows.count).client_id        := :old.client_id
         l_audit_rows(l_audit_rows.count).client_name      := :old.client_name
         ... all the fields
     end if;
   end after each row;
 
   after statement is
   begin
     --
     -- then at completion, do a single insert of all the rows into our audit table
     --
 
     forall i in 1 .. l_audit_rows.count
        insert into audit_table values l_audit_rows(i);
        l_audit_rows.delete;
   end after statement;
   end;
   /

【讨论】:

    猜你喜欢
    • 2010-10-15
    • 2010-09-17
    • 1970-01-01
    • 2021-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    相关资源
    最近更新 更多