【问题标题】:Return deleted rows in a cursor in PL/SQL在 PL/SQL 中的游标中返回已删除的行
【发布时间】:2021-01-21 05:54:25
【问题描述】:

我有一个要求,我想在 sys_refcursor 中返回已删除的记录。我可以在删除语句之前检索游标中的数据,但是在删除语句之后有什么方法可以检索它们?我的意思是首先删除然后打开 sys_refcursor 以获取已删除记录的过程。

【问题讨论】:

  • 我会说应该反过来。如果您删除了 行,那么它们就不存在了——您如何检索它们?除非有一个数据库触发器将删除的行存储到另一个表中(可能带有时间戳),以便您可以做您想做的事情。

标签: plsql cursor sys-refcursor bulk-collect


【解决方案1】:

可能有几种选择,但一种选择是使用RETURNING 和BULK COLLECT。这是一个简单的例子。

CREATE TABLE t (
       a NUMBER,
       b VARCHAR2(10),
       c DATE
);

INSERT INTO t VALUES (1, 'a', SYSDATE);
INSERT INTO t VALUES (2, 'b', SYSDATE);
INSERT INTO t VALUES (3, 'c', SYSDATE);
INSERT INTO t VALUES (4, 'd', SYSDATE);
INSERT INTO t VALUES (5, 'e', SYSDATE);

CREATE OR REPLACE TYPE tt AS OBJECT (
       a NUMBER,
       b VARCHAR2(10),
       c DATE
);

CREATE OR REPLACE TYPE tt_tab AS TABLE OF tt;

DECLARE
  v_tt_tab tt_tab;
  v_tt     tt;
  v_cur    SYS_REFCURSOR;
BEGIN
  DELETE FROM t
   WHERE a < 4
  RETURNING tt(a, b, c) BULK COLLECT INTO v_tt_tab;

  OPEN v_cur FOR
    SELECT tt(a,
              b,
              c)
      FROM TABLE(v_tt_tab);

  LOOP
    FETCH v_cur
      INTO v_tt;
    EXIT WHEN v_cur%NOTFOUND;
  
    dbms_output.put_line(v_tt.a || ' ' || v_tt.b || ' ' || v_tt.c);
  END LOOP;

  CLOSE v_cur;
END;
/

/*
1 a 07-OCT-20
2 b 07-OCT-20
3 c 07-OCT-20
*/

通过创建与我们想要保留的数据匹配的对象和该对象的表,我们可以将其返回到集合中,然后使用 TABLE 函数轻松地将其转换回游标。

您需要小心删除多少行,因为您不想耗尽内存。如果您删除的行数超过几百行,我会谨慎使用这种方法。

另一个建议可能是使用 GTT,插入您计划删除的行,然后使用 GTT 作为“键”从第一个表中删除。

CREATE GLOBAL TEMPORARY TABLE t_gtt (
       a NUMBER,
       b VARCHAR2(10),
       c DATE
);

DECLARE
  v_tt_tab tt_tab;
  v_tt     tt;
  v_cur    SYS_REFCURSOR;
BEGIN
  INSERT INTO t_gtt
    SELECT *
      FROM t
     WHERE a < 4;

  DELETE FROM t
   WHERE EXISTS (SELECT NULL
            FROM t_gtt
           WHERE t_gtt.a = t.a);

  OPEN v_cur FOR
    SELECT tt(a,
              b,
              c)
      FROM t_gtt;

  LOOP
    FETCH v_cur
      INTO v_tt;
    EXIT WHEN v_cur%NOTFOUND;
  
    dbms_output.put_line(v_tt.a || ' ' || v_tt.b || ' ' || v_tt.c);
  END LOOP;

  CLOSE v_cur;
END;
/

如果您打算删除大量行,此选项可能会更好。我再次使用了我的tt 对象,但你真的不需要它。它只是让循环转储SYS_REFCURSOR 的内容变得更容易。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-14
    • 2019-05-03
    • 2016-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多