假设一个雇员表类似于Oracle HR示例架构中的雇员表。它包括以下内容:
CREATE TABLE employees
(
employee_id NUMBER(6) NOT NULL PRIMARY KEY,
...
manager_id NUMBER(6)
);
ALTER TABLE employees_copy
ADD CONSTRAINT employee_manager_fk
FOREIGN KEY (manager_id)
REFERENCES employees(employee_id);
我想避免互惠关系:如果ID为110的员工向ID为110的员工报告,我不想允许员工110向ID为100的员工报告。我想防止以下情况:
employee_id manager_id
100 110
110 100
我认为这没有约束性,因为这将需要子查询。因此,我认为触发是必要的。这仅是UPDATE上的一个问题,因为使用DELETE或INSERT无法创建这种情况。我创建了以下触发器,即可完成工作:
CREATE OR REPLACE TRIGGER employees_bu_trigger
BEFORE
UPDATE OF manager_id
ON employees
FOR EACH ROW
DECLARE
l_managers_manager_id employees.manager_id%TYPE;
reciprocal_managers EXCEPTION;
-- This prevents ORA-04091: table C##HR.EMPLOYEES
-- is mutating, trigger/function may not see it
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
SELECT manager_id
INTO l_managers_manager_id
FROM employees
WHERE employee_id = :new.manager_id;
IF l_managers_manager_id = :new.employee_id THEN
RAISE reciprocal_managers;
END IF;
EXCEPTION
WHEN reciprocal_managers THEN
-- re-raise the error to be caught by calling code
RAISE_APPLICATION_ERROR(-20102,
'Employees cannot manage each other.');
END;
但是我担心PRAGMA AUTONOMOUS_TRANSACTION;
行。这是防止出现ORA-04091
错误的必要条件,但它使我认为这里可能还有其他潜在的问题。
此触发条件还好吗?还是会有无法预料的负面影响?
答案 0 :(得分:0)
PRAGMA AUTONOMOUS_TRANSACTION
的危险在于它在单独的事务中运行。因此,如果您的活动事务只是添加了一个条目,然后在提交您进行更新之前,此触发器将不会看到插入的记录,并且将允许违反您的规则的记录。其他并发用户也会发生此问题(即,如果用户a在事务中进行了更新,而用户b在进行了更新,则对于他们两个都将通过检查,但是一旦他们都提交了错误状态,即使两个检查都通过了,错误状态也可能存在。 )
同样,尽管提供的示例没有此问题(如Oracle's documentation指出A writer never blocks a reader.
),但是如果在触发器中执行任何写操作,则存在死锁的危险。
如果没有FOR EACH ROW,可能会将您的支票重构为AFTER触发器。
这将消除死锁的危险以及在单个用户案例中通过测试的危险。 但不会消除多用户问题。
Jon Heller对多级循环的评论也可能与我们的特定问题有关,但是对支票所做的任何更改都不会影响您明确询问的问题。