Order by在触发器的Oracle版本中不起作用

时间:2019-07-19 14:51:06

标签: sql oracle

我将触发器从SQL转换为Oracle 10时结合了JOIN,ORDER BY和ROWNUM。 需要从脚本中删除ORDER BY才能正确创建触发器,但我需要它。

CREATE OR REPLACE TRIGGER Update_Quantity 
AFTER INSERT
ON MediaUsed
FOR EACH ROW
Begin
   UPDATE          Inventory 
   SET             Quantity = Quantity - 1 
   WHERE ID = ( SELECT IV1.ID FROM Inventory IV1
             INNER JOIN (
                        SELECT Distinct ItemNo, Item, :NEW.Plant AS Plant  FROM Inventory    
                        WHERE (Not :NEW.IsChanged = 0) AND  Inventory.ID = :NEW.ID_Inventory
                       )  IV2 ON IV1.Item = IV2.Item AND IV1.ItemNo = IV2.ItemNo AND IV1.Plant = IV2.Plant
                WHERE IV1.Quarantined = 0  AND IV1.Quantity > 0  AND IV1.ExpiryDate > SYS_EXTRACT_UTC(SYSTIMESTAMP) AND ROWNUM = 1
                ORDER BY IV1.ID
              );
End;

Warning: TRIGGER created with compilation errors.

我试图包装查询并将ORDER By放在外面,但是没有用。我发现ROWNUM不是最好的方法。 我在搜索中发现RANK()OVER是更好的解决方案,并编写了以下内容,但触发器仍然存在一些问题:

CREATE OR REPLACE TRIGGER Update_Quantity 
AFTER INSERT
ON MediaUsed
FOR EACH ROW
Begin
   UPDATE          Inventory 
   SET             Quantity = Quantity - 1 
   WHERE ID = ( SELECT IV1.ID, RANK() OVER (ORDER BY IV1.ID) sal_rank FROM Inventory IV1
             INNER JOIN (
                        SELECT Distinct ItemNo, Item, :NEW.Plant AS Plant  FROM Inventory    
                        WHERE (Not :NEW.IsChanged = 0) AND  Inventory.ID = :NEW.ID_Inventory
                       )  IV2 ON IV1.Item = IV2.Item AND IV1.ItemNo = IV2.ItemNo AND IV1.Plant = IV2.Plant
                WHERE IV1.Quarantined = 0  AND IV1.Quantity > 0  AND IV1.ExpiryDate > SYS_EXTRACT_UTC(SYSTIMESTAMP) AND sal_rank <= 1 --ROWNUM = 1
              );
End;

Warning: TRIGGER created with compilation errors.

删除ORDER BY可以修复触发器,但是我需要它!

CREATE OR REPLACE TRIGGER Update_Quantity 
AFTER INSERT
ON MediaUsed
FOR EACH ROW
Begin
   UPDATE          Inventory 
   SET             Quantity = Quantity - 1 
   WHERE ID = ( SELECT IV1.ID FROM Inventory IV1
             INNER JOIN (
                        SELECT Distinct ItemNo, Item, :NEW.Plant AS Plant  FROM Inventory    
                        WHERE (Not :NEW.IsChanged = 0) AND  Inventory.ID = :NEW.ID_Inventory
                       )  IV2 ON IV1.Item = IV2.Item AND IV1.ItemNo = IV2.ItemNo AND IV1.Plant = IV2.Plant
                WHERE IV1.Quarantined = 0  AND IV1.Quantity > 0  AND IV1.ExpiryDate > SYS_EXTRACT_UTC(SYSTIMESTAMP) AND ROWNUM = 1
                --ORDER BY IV1.ID
              );
End;

Trigger created.

所以我需要ORDER BY,并且需要使用该JOIN获取第一行。如果有什么办法可以使我对TOAD中的问题有更多的描述,那也是完美的。

2 个答案:

答案 0 :(得分:1)

您的第二个触发器看起来应该可以工作,但是您的查询将两个值返回到=子句:

WHERE ID = ( SELECT IV1.ID, RANK() OVER (ORDER BY IV1.ID) sal_rank

将其推入子查询以仅获得排名靠前的ID怎么样?像这样:

CREATE OR REPLACE TRIGGER OR1_EM.Update_Quantity 
AFTER INSERT
ON MediaUsed
FOR EACH ROW
Begin
   UPDATE          Inventory 
   SET             Quantity = Quantity - 1 
   WHERE ID = (SELECT ID
              FROM 
             ( SELECT IV1.ID, RANK() OVER (ORDER BY IV1.ID) sal_rank FROM Inventory IV1
             INNER JOIN (
                        SELECT Distinct ItemNo, Item, :NEW.Plant AS Plant  FROM Inventory    
                        WHERE (Not :NEW.IsChanged = 0) AND  Inventory.ID = :NEW.ID_Inventory
                       )  IV2 ON IV1.Item = IV2.Item AND IV1.ItemNo = IV2.ItemNo AND IV1.Plant = IV2.Plant
                WHERE IV1.Quarantined = 0  AND IV1.Quantity > 0  AND IV1.ExpiryDate > SYS_EXTRACT_UTC(SYSTIMESTAMP) AND sal_rank <= 1 --ROWNUM = 1
              )
              WHERE sal_rank = 1 );
End;

此外,请注意,如果该子查询多次返回该ID,则RANK可以为给定值返回多行。如果可能的话,您需要将其减少到仅返回=子句的一行,最简单的方法是使用MAX():

CREATE OR REPLACE TRIGGER OR1_EM.Update_Quantity 
AFTER INSERT
ON MediaUsed
FOR EACH ROW
Begin
   UPDATE          Inventory 
   SET             Quantity = Quantity - 1 
   WHERE ID = (SELECT MAX(ID)
              FROM 
             ( SELECT IV1.ID, RANK() OVER (ORDER BY IV1.ID) sal_rank FROM Inventory IV1
             INNER JOIN (
                        SELECT Distinct ItemNo, Item, :NEW.Plant AS Plant  FROM Inventory    
                        WHERE (Not :NEW.IsChanged = 0) AND  Inventory.ID = :NEW.ID_Inventory
                       )  IV2 ON IV1.Item = IV2.Item AND IV1.ItemNo = IV2.ItemNo AND IV1.Plant = IV2.Plant
                WHERE IV1.Quarantined = 0  AND IV1.Quantity > 0  AND IV1.ExpiryDate > SYS_EXTRACT_UTC(SYSTIMESTAMP) AND sal_rank <= 1 --ROWNUM = 1
              )
              WHERE sal_rank = 1 );
End;

答案 1 :(得分:0)

最后,我通过包装脚本并将ROWNUM放在外面来解决了这个问题:

CREATE OR REPLACE TRIGGER OR1_FG.Update_Quantity 
AFTER INSERT
ON OR1_FG.MediaUsed
FOR EACH ROW
Begin
   UPDATE          OR1_FG.Inventory 
   SET             Quantity = Quantity - 1 
   WHERE ID = 
    (
     select ID from 
            (
             SELECT IV1.ID FROM OR1_FG.Inventory IV1 
             INNER JOIN (
             SELECT Distinct LotNumber, Item, :NEW.Plant AS Plant  
             FROM OR1_FG.Inventory WHERE (Not :NEW.IsChanged = 0
             ) 
             AND  OR1_FG.Inventory.ID = :NEW.ID_Inventory
             )  IV2 ON IV1.Item = IV2.Item AND IV1.LotNumber = IV2.LotNumber AND IV1.Plant = IV2.Plant
                WHERE IV1.Quarantined = 0  AND IV1.Quantity > 0  AND IV1.ExpiryDate > SYS_EXTRACT_UTC(SYSTIMESTAMP) ORDER BY IV1.ID) WHERE ROWNUM = 1);
End;

感谢您的所有贡献。