好的,我有两个表 - ORDERS和ORDERLINES - 它们基本上有相同的问题,每个表都有触发器来解决这个问题。问题是除了具有表级唯一性的PK之外,在名为RECID的字段上还有另一个字段RECNO,它需要与另一个字段相关才是唯一的。
表格与FK相关如下:
ORDERS.WAREHOUSEID > WAREHOUSES.CUSTOMERID > CUSTOMERS
和
ORDERSLINES.ORDERID > ORDERS
在ORDERS
和ORDERSLINES
我有BEFORE INSERT
个触发器来分配特定领域的唯一 RECNO 。
在ORDERS
中, RECNO 在CUSTOMERS
记录范围内必须是唯一的。
在ORDERLINES
中, RECNO 在ORDERS
记录范围内必须是唯一的。
ORDERS
上的触发器完全正常。插入新订单后,会在其所属的客户中为其分配下一个唯一的 RECNO 。
另一方面,ORDERLINES
上的触发器应该在其所属的顺序中分配下一个唯一的 RECNO ,从而引发可怕的 {ORA-04091 :表ORDERLINES正在变异,触发器/函数可能看不到它} 异常。
以下是有效的触发器:
CREATE OR REPLACE TRIGGER ORDERS_BI
BEFORE INSERT ON ORDERS
FOR EACH ROW
DECLARE
CUSTID WAREHOUSES.CUSTOMERID%TYPE;
BEGIN
SELECT MIN(CUSTOMERID) INTO CUSTID FROM WAREHOUSES
WHERE NVL(WARE_ID, '-') = NVL(:NEW.WAREHOUSEID, '-');
SELECT NVL(MAX(RECNO), 0) + 1
INTO :NEW.RECNO
FROM deploy.ORDERS O
LEFT JOIN deploy.WAREHOUSES W
ON NVL(W.REC, '-') = NVL(O.WAREHOUSEID, '-')
WHERE NVL(W.CUSTOMERID, '-') = NVL(CUSTID, '-');
END;
这是 NOT 工作的触发器:
CREATE OR REPLACE TRIGGER ORDERLINES_BI
BEFORE INSERT ON ORDERLINES
FOR EACH ROW
DECLARE
nORDERID ORDERLINES.ORDERID%TYPE;
BEGIN
SELECT MIN(ORDERID) INTO nORDERID FROM REVORDERS
WHERE ORDERID = :NEW.ORDERID;
SELECT NVL(MAX(RECNO), 0) + 1
INTO :NEW.RECNO
FROM deploy.ORDERLINES L
LEFT JOIN deploy.ORDERS O
ON O.ORDERID = L.ORDERID
WHERE O.ORDERID = nORDERID;
END;
有人可以解释为什么第一个有效,第二个没有? 是否有某种方法可以重新编写第二个以使其有效?
答案 0 :(得分:4)
我先看了你的代码,而不是你的解释。我的第一个想法是“这个人试图伪造sequence。”这显然不是你问题的答案,但这就是你首先遇到麻烦的原因。
当您在伪造序列时出现问题时,显而易见的解决方案是使用真实的解决方案。
当您尝试从触发触发器的表中读取时发生Nicholas has already noted ORA-04091。有各种方法可以避免这种情况,其中大多数都避免尝试做一些有点时髦的事情。但是,它们不会影响错误的根本原因;那就是你做错了错误。此错误通常表示两件事中的一件或两件:
第一个解决方案是将逻辑移动到一个包,它具有删除一层混淆的额外好处。第二种解决方案是正确地规范化数据库。
在您的情况下,根据您提供的信息,您的数据模型似乎没问题,但正如我所说,我不同意实施。
这为您提供了四个选项来解决您的问题,我会按顺序详细说明
我不打算讨论第3点,因为你可以自己做。尼古拉斯部分地涵盖了第4点,我不打算提倡我不同意的事情。这留下了第1点和第2点。你说
在ORDERS中,RECNO需要在客户领域内独一无二 记录。
这不是你实现它的方式。您的代码会在RECNO
记录的范围内CUSTOMERS
连续。 ORDERS
和ORDERLINES
的主键在<{1}}记录的范围内定义唯一
这本身意味着选项1最适合您。完全删除触发器;表的主键已经完成了你需要的一切。这也使选项2无效;如果你添加一个序列,那么它基本上是一个单独的主键。
我没有理由认为您需要订单才能在每个客户中连续独特;为什么这么做呢?
答案 1 :(得分:2)
您收到该错误,因为第二个触发器在修改表时尝试读取表。当父表上的触发器导致引用外键的子表上的插入时,也会发生这种情况。 快速创建视图并尝试使用而不是触发器。 另请参阅Tom's如何处理变异问题的示例。 此外,如果按原样保留第二个触发器,任何插入到your_table select .. from table 都会引发变异错误。例如:
此插入将起作用
insert into ORDERLINES(column1, column2... columnN)
values(val1, val2,..., valN)
但是这个不会。
insert into ORDERLINES(column1, column2... columnN)
select val, val..val from table