想象下面描述的两个表格。 这些表(transactionnumber列)之间存在“关系”,但它们不能符合外键约束,因为它们不是唯一的。
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中删除所有具有相同transactionnumber的行。
所以我创建了以下触发器:
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 ...
答案 0 :(得分: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;