【问题标题】:Simulating DELETE ON CASCADE behaviour using trigger使用触发器模拟 DELETE ON CASCADE 行为
【发布时间】:2014-09-26 08:00:00
【问题描述】:

请想象下面描述的两个表格。 这些表(事务编号列)之间存在“关系”,但它们不能符合外键约束,因为它们不是唯一的。

CREATE TABLE COMMAND_DATA
(
  TRANSACTIONNUMBER NUMBER(19) NOT NULL,
  KEYNAME VARCHAR2(255) NOT NULL,
  DATA VARCHAR2(255)
  CONSTRAINT PK_COMMAND_DATA PRIMARY KEY (COMMANDTRANSACTIONNUMBER, KEYNAME)
);

CREATE TABLE COMMAND
(
  TRANSACTIONNUMBER NUMBER(19) NOT NULL,
  COMMANDTYPE NUMBER(10) NOT NULL,
  TRANSACTIONTIMESTAMP NUMBER(19) NOT NULL,
  SOURCEID VARCHAR2(255),
  CONSTRAINT PK_COMMAND PRIMARY KEY (TRANSACTIONNUMBER, COMMANDTYPE)
);

现在,假设在 COMMAND 表中有 10 行包含 transactionnumber = 1,在 COMMAND_DATA 表中有 3 行也包含 transactionnumber = 1。这些数据每次由批处理填充一次。现在我的系统正在处理和删除 COMMAND 表中的行。

我想要实现的是,从 COMMAND 表中删除最后一行 transactionnumber = 1 后,所有具有相同事务号的行都将从表 COMMAND_DATA 中删除。

所以我创建了以下触发器:

CREATE TRIGGER CLEAN_COMMAND_DATA
AFTER DELETE ON COMMAND FOR EACH ROW 

DECLARE
   pragma autonomous_transaction;
   v_count number(10);
BEGIN
   select count(*) into v_count from command where TRANSACTIONNUMBER = :old.TRANSACTIONNUMBER;

   if v_count = 0 then
      delete from COMMAND_DATA where TRANSACTIONNUMBER = :old.TRANSACTIONNUMBER;
   end if;
END;

但它不起作用,因为 select 语句也在计算被删除的行数,所以永远不会出现 count(*) 返回零的情况。

如何调整触发器?还是有更好的解决方案?此处无法使用 DELETE ON CASCADE,因为我无法使用 FK...

【问题讨论】:

  • 这个看起来很别扭的设计,说明少了点什么。在我看来,它看起来应该有一个 TRANSACTION 表,其 PK 是 TRANSACTIONNUMBER,FK 从 COMMAND 和 COMMAND_DATA 到 TRANSACTION - 然后在完成事务时,从 TRANSACTION 中删除级联到从 COMMAND 和 COMMAND_DATA 中删除。或者 COMMAND_DATA 应该 FK 到 COMMAND - 可能需要 COMMAND 上的合成 PK 以允许这样做。也许 COMMAND.TRANSACTIONNUMBER、TRANSACTIONTIMESTAMP 和可能的 SOURCE_ID 应该在 TRANSACTION 表上,但这当然只是一个猜测。祝你好运。
  • 你好 Bob,我明白你的意思,在任何其他情况下我都会同意。但是我稍微简化了这个例子来强调这一点——主键由更多的列组成,数据也包含更多的列(不幸的是一些大的二进制数据)。这个解决方案不是我设计的,但有充分的理由。因此,COMMAND_DATA 中只有一行,而 COMMAND 中至少有数千行。删除的过程(以及实际的业务逻辑)使您无法利用您的想法。这也是我们想做的第一件事——调整设计以更好地适应,但是......

标签: sql oracle triggers constraints


【解决方案1】:

我假设您已经发现,您不能从定义行级触发器的同一个表中进行选择;它会导致表变异异常。

您已尝试使用自主事务杂注解决此问题,尽管它消除了异常,但实际上只是涵盖了您方法中的错误。您需要将处理移至语句级触发器。在 Oracle 11g 及更高版本中,您可以执行以下操作:

CREATE OR REPLACE TRIGGER clean_command_data
  FOR DELETE ON command
  COMPOUND TRIGGER

  -- Table to hold identifiers of inserted/updated transactions
  g_transactionNumbers sys.odcinumberlist;

BEFORE STATEMENT 
IS
BEGIN
-- Reset the internal transactions table
 g_transactionNumbers := sys.odcinumberlist();
END BEFORE STATEMENT; 

AFTER EACH ROW
IS
BEGIN
  -- Store the inserted/updated transactions
  g_transactionNumbers.EXTEND;
  g_transactionNumbers(g_transactionNumbers.LAST) := :old.transactionNumber;
END AFTER EACH ROW;

AFTER STATEMENT
IS
  CURSOR csr_commands
  IS
    SELECT DISTINCT
           tno.column_value transactionNumber
    FROM TABLE(g_transactionNumbers) tno
         LEFT OUTER JOIN command cmd
           ON (cmd.transactionNumber = tno.column_value)
    WHERE cmd.transactionNumber IS NULL;
BEGIN
  -- Check if for any deleted transaction there exists no more commands
  FOR r_command IN csr_commands LOOP
    DELETE FROM command_data
    WHERE transactionNumber = r_command.transactionNumber;
  END LOOP;
END AFTER STATEMENT;

END;

【讨论】:

  • 你好@DrabJay,你的假设是正确的:-)。我试试看。
  • 我在这一行“g_transactionNumbers := g_transactionNumbers();”上收到“错误(8,26):PLS-00355:在此上下文中不允许使用 pl/sql 表” - 语句被忽略。知道为什么吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-02
  • 2011-09-21
  • 2010-12-27
  • 1970-01-01
  • 1970-01-01
  • 2022-06-25
相关资源
最近更新 更多