Reputation: 3137
imagine two tables described below please. There is an "relationship" between those tables (transactionnumber columns) but they cannot conform foreign key constraint because they are not unique.
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)
);
Now, imagine there are for example 10 rows containing transactionnumber
= 1 in COMMAND table and there are 3 rows containing also transactionnumber
= 1 in COMMAND_DATA table. These data is filled by batch process once per time. Now my system is processing and deleting rows one by one from COMMAND table.
What I want to achieve is, that after deleting last row with transactionnumber
= 1 from COMMAND table, all rows with same transactionnumber will be deleted from table COMMAND_DATA.
So I created following trigger:
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;
But it doesn't work because the select statement is also counting rows which are being deleted, so there is never situation where count(*) returns zero.
How could I adjust that trigger ? Or is there better solution ? Using DELETE ON CASCADE is not possible here because I can't use FK...
Upvotes: 1
Views: 469
Reputation: 3099
As I assume you have discovered, you cannot select from the same table that a row-level trigger is defined against; it causes a table mutating exception.
You have attempted to get round this using the autonomous transaction pragma which, although it remove the exception, actually just covers the mistake in your methodology. You need to move the processing to a statement level trigger. In Oracle 11g and above you could do the following:
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;
Upvotes: 1