【问题标题】:How to access full OLD data in SQL Trigger如何在 SQL 触发器中访问完整的旧数据
【发布时间】:2021-02-05 21:17:33
【问题描述】:

我有一个触发器,其目的是在特定表上有 DELETE 时触发,并将删除的数据以 json 格式插入到另一个表中。 如果我明确指定每一列,触发器工作正常。有没有办法访问整个表格行? 这是我的代码。

TRIGGER1 
AFTER DELETE 
ON QUESTION
FOR EACH ROW
DECLARE 
json_doc CLOB;
BEGIN
    select json_arrayagg (  
    json_object ('code' VALUE :old.id,
                      'name' VALUE :old.text,
                      'description' VALUE :old.text) returning clob   
    ) into json_doc
    from dual;
    
    PROCEDURE1(json_doc);
END;

这很好用。但是,我想要的是这样的。我不想明确指定每一列,而是想转换整个 :OLD 数据

TRIGGER1 
AFTER DELETE 
ON QUESTION
FOR EACH ROW
DECLARE 
json_doc CLOB;
BEGIN
    select json_arrayagg (  
    json_object (:old) returning clob   
    ) into json_doc
    from dual;
    
    PROCEDURE1(json_doc);
END;

请给点建议。

【问题讨论】:

  • 您必须指定每一列。 :New 和 :Old 表示触发器中的伪行,但是 Oracle 不允许对它们进行行处理,只允许对单个列进行处理。

标签: sql database oracle plsql triggers


【解决方案1】:

简短而正确的答案是您不能。我们在应用程序中有几个表,我们在其中执行此操作,并且开发人员在添加列时负责更新触发器:这是通过代码审查强制执行的,并且可能是这种情况下最干净的解决方案。

长答案是您可以接近,但出于以下几个原因我不会在生产中这样做:

  1. 触发器不利于性能
  2. 触发器对于代码清晰度来说很糟糕
  3. 这需要使用闪回查询再次读取该行,因此
    1. 您没有从当前事务中获取此行的值:如果您更新事务中的行然后将其删除,JSON 将显示更新前的值
    2. 从 UNDO 读取会降低性能
    3. UNDO 可能不可用,您的触发器可能会失败
    4. 您的用户需要权限才能执行闪回查询
    5. 您的数据库需要满足所有条件才能支持闪回查询
  4. 删除大量行会导致ROWID 集合变大并消耗 PGA

可能还有更多原因,但为了“可以做到”,你去吧......

DROP TABLE t1;
DROP TABLE t2;
DROP TRIGGER t1_ad;

CREATE TABLE t1 (
       id NUMBER,
       name VARCHAR2(100),
       description VARCHAR2(100)
);

CREATE TABLE t2 (
       dt TIMESTAMP(9),
       json_data CLOB
);

INSERT INTO t1 VALUES (1, 'A','aaaa');
INSERT INTO t1 VALUES (2, 'B','bbbb');
INSERT INTO t1 VALUES (3, 'C','cccc');
INSERT INTO t1 VALUES (4, 'D','dddd');

CREATE OR REPLACE TRIGGER t1_ad
  FOR DELETE ON t1
  COMPOUND TRIGGER

  TYPE t_rowid_tab IS TABLE OF ROWID;
  v_rowid_tab t_rowid_tab := t_rowid_tab();

  AFTER EACH ROW IS
  BEGIN
    v_rowid_tab.extend;
    v_rowid_tab(v_rowid_tab.last) := :old.rowid;
  END AFTER EACH ROW;

  AFTER STATEMENT IS
    v_scn       v$database.current_scn := dbms_flashback.get_system_change_number;
    v_json_data CLOB;
    v_sql       CLOB;
  BEGIN
    FOR i IN 1 .. v_rowid_tab.count
    LOOP
      SELECT 'SELECT json_arrayagg(json_object(' ||
             listagg('''' || lower(t.column_name) || ''' VALUE ' ||
                     lower(t.column_name),
                     ', ') within GROUP(ORDER BY t.column_id) || ') RETURNING CLOB) FROM t1 AS OF SCN :scn WHERE rowid = :r'
        INTO v_sql
        FROM user_tab_columns t
       WHERE t.table_name = 'T1';
    
      EXECUTE IMMEDIATE v_sql
        INTO v_json_data
        USING v_scn, v_rowid_tab(i);
    
      INSERT INTO t2
      VALUES
        (current_timestamp,
         v_json_data);
    END LOOP;
  END AFTER STATEMENT;
END t1_ad;
/

UPDATE t1
   SET NAME = 'zzzz' -- not captured
 WHERE id = 2;
DELETE FROM t1 WHERE id < 3;

SELECT *
  FROM t2;
-- 13-NOV-20 01.08.15.955426000 PM  [{"id":1,"name":"A","description":"aaaa"}]
-- 13-NOV-20 01.08.15.969755000 PM  [{"id":2,"name":"B","description":"bbbb"}]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-30
    • 2013-01-09
    • 2023-04-07
    • 2013-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多