如何防止互惠的主键-外键关系?

时间:2019-07-19 22:44:04

标签: plsql triggers check-constraints

假设一个雇员表类似于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错误的必要条件,但它使我认为这里可能还有其他潜在的问题。

此触发条件还好吗?还是会有无法预料的负面影响?

1 个答案:

答案 0 :(得分:0)

PRAGMA AUTONOMOUS_TRANSACTION的危险在于它在单独的事务中运行。因此,如果您的活动事务只是添加了一个条目,然后在提交您进行更新之前,此触发器将不会看到插入的记录,并且将允许违反您的规则的记录。其他并发用户也会发生此问题(即,如果用户a在事务中进行了更新,而用户b在进行了更新,则对于他们两个都将通过检查,但是一旦他们都提交了错误状态,即使两个检查都通过了,错误状态也可能存在。 ) 同样,尽管提供的示例没有此问题(如Oracle's documentation指出A writer never blocks a reader.),但是如果在触发器中执行任何写操作,则存在死锁的危险。 如果没有FOR EACH ROW,可能会将您的支票重构为AFTER触发器。 这将消除死锁的危险以及在单个用户案例中通过测试的危险。 不会消除多用户问题。

Jon Heller对多级循环的评论也可能与我们的特定问题有关,但是对支票所做的任何更改都不会影响您明确询问的问题。