Oracle pl/sql DELETE正在删除所有行,而不是所选行

Oracle pl/sql DELETE正在删除所有行,而不是所选行,oracle,plsql,Oracle,Plsql,我有扳机: create or replace TRIGGER JACKET_DELETE BEFORE DELETE ON JACKET FOR EACH ROW BEGIN DELETE FROM PORT WHERE EXISTS (SELECT * FROM port LEFT JOIN device on port.fkdevice = device.pkid where port.fkjacket = :old.pkid and device

我有扳机:

create or replace
TRIGGER JACKET_DELETE 
BEFORE DELETE ON JACKET 
FOR EACH ROW 
BEGIN
  DELETE FROM PORT
  WHERE EXISTS
    (SELECT * FROM port LEFT JOIN device on port.fkdevice = device.pkid
     where port.fkjacket = :old.pkid
     and device.fkdevice_type = 1);

  UPDATE PORT
  set port.fkjacket = null, port.fkport = null
  WHERE EXISTS
(SELECT port.fkjacket, port.fkport FROM port LEFT JOIN device on port.fkdevice = device.pkid
     where port.fkjacket = :old.pkid
     and device.fkdevice_type <> 1);
END;
由于某种原因,当删除中的where匹配时,它会删除整个端口表!我认为我的SQL是正确的,但显然不是,我看不出有什么问题。有人能看到促使它这样做的问题吗

当更新匹配时,一切正常

表结构:
设备、护套和端口的端口链接

当fkjacket字段匹配:old.pkid时,您的删除操作将删除所有内容,因为您没有限制删除其他内容。如果EXISTS子句返回一行,则所有内容都将继续

将此更改为类似以下内容:

  DELETE FROM PORT
  WHERE fkjacket IN
    (SELECT port.fkjacket FROM port LEFT JOIN device on port.fkdevice = device.pkid
     where port.fkjacket = :old.pkid
     and device.fkdevice_type = 1);
这将删除端口表中fkjacket位于选择列表中返回的fkjacket值列表中的所有行

你确定你的更新工作正常吗?在我看来,你应该得到同样的行为与它-所有行更新

编辑:

由于您的更新以同样的方式失败,我建议将其更改为:

  UPDATE PORT
  SET port.fkjacket = null, port.fkport = null
  WHERE fkjacket IN
       (SELECT port.fkjacket 
          FROM port LEFT JOIN device on port.fkdevice = device.pkid
         WHERE port.fkjacket = :old.pkid
           AND device.fkdevice_type <> 1);

这将更新端口表中的所有行,其中fkjacket位于选择列表中返回的fkjacket值列表中。

您的删除操作将引用端口表两次。为了澄清这一点,我们首先修改该语句以包含表别名:

  DELETE FROM PORT p1
  WHERE EXISTS
    (SELECT * FROM port p2 LEFT JOIN device on p2.fkdevice = device.pkid
     where p2.fkjacket = :old.pkid
     and device.fkdevice_type = 1);
请注意,子查询与p1不相关。换句话说,此子查询的结果对于考虑删除的端口中的每一行都是相同的。所以你要么删除所有行,要么不删除行

当外部表上有一个非连接谓词时,使用左连接也是很奇怪的。但这在最坏的情况下是一个效率问题,更可能只是让任何阅读您的代码的人感到困惑

我相信你想要的是:

DELETE FROM PORT
WHERE fkjacket = :old.pkid
AND EXISTS
  (SELECT NULL FROM device
   WHERE device.pkid = port.fkdevice
     AND device.fkdevice_type=1);
更新似乎也有同样的问题;即使它目前给了你预期的结果,我打赌这只是运气,因为你测试的数据。我认为可以简化为:

UPDATE PORT
  set port.fkjacket = null, port.fkport = null
  WHERE port.fkjacket = :old.pkid
    AND EXISTS
    (SELECT NULL FROM device
       WHERE port.fkdevice = device.pkid
       AND device.fkdevice_type <> 1);

注意,EXISTS操作符并不关心它的子查询是否返回了任何列;是否返回所有行。

是的,我做了更彻底的测试,您是正确的,更新所有行。要解决这个问题也需要同样的修改?是的。如果我正确理解您的代码,您必须将更新限制为fkjacket的特定值。fkjacket的特定值是要删除的行的pkid,而device的值。fkdevice_类型表示您有两个端口,都链接到同一个护套,但连接到不同的设备;在这些设备中,一个是1型,另一个不是。我相信这里给出的删除操作将从端口删除两行,而不仅仅是链接到类型1的设备的那一行。@Dave事实上,等等,我的数据库有点大,看起来结果可能仍然存在。还有另一个选项,或者你是说“d饼干”的答案是行不通的,我正在测试它,以找出我自己不确定DCookie的答案是否有效。看来你已经决定了。不过,我对此有些担心。端口中具有相同FKJACKET值的所有内容是否也具有相同的FKDEVICE值?如果不是,那么那里的子查询可能会给出误报;是否返回所有行这就是为什么使用select null?我猜这比选择任何列都要快?这可能不是真的快,因为Oracle可以在不需要列引用的情况下基本上消除列引用。但是我的习惯是使用SELECT NULL来表明没有选择任何特定的数据,我认为值得指出的是,在代码中,您在子查询中选择了多个列。是的,在更新中,我认为我需要选择集合正在更新的列,在删除中,因为。。。嗯,我不确定我这样做的逻辑。我不知道null是select的一个选项。