pl/sql DELETE删除所有行而不是选定的行

Ash*_*mum 4 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.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;
Run Code Online (Sandbox Code Playgroud)

出于某种原因,当wheredelete比赛中,它会删除整个port表!我认为我的SQL是正确的,但显然它不是,我看不出它有什么问题.任何人都可以看到这样做的问题吗?

update比赛中,一切正常.


表结构:端口链接到设备,夹克和端口

Dav*_*sta 8

您的DELETE两次引用PORT表.为了澄清,我们首先修改语句以包含表别名:

  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);
Run Code Online (Sandbox Code Playgroud)

请注意,子查询与之无关p1.换句话说,对于PORT中考虑删除的每一行,此子查询的结果都是相同的.所以你要么删除所有行,要么删除没有行.

(这也是奇怪的是,你使用LEFT JOIN时,你对外部表的非连接谓词.但是,这是在最坏的情况出现的效率问题,更可能只是迷惑人阅读你的代码.)

我相信你想要的是:

DELETE FROM PORT
WHERE fkjacket = :old.pkid
AND EXISTS
  (SELECT NULL FROM device
   WHERE device.pkid = port.fkdevice
     AND device.fkdevice_type=1);
Run Code Online (Sandbox Code Playgroud)

UPDATE似乎有同样的问题; 即使它目前给你预期的结果,我敢打赌,由于您正在测试的数据,这只是运气.我认为它可以简化为:

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);
Run Code Online (Sandbox Code Playgroud)

请注意,如果子查询返回任何列,则EXISTS运算符不关心它是什么.只是是否返回行.


DCo*_*kie 5

当fkjacket字段匹配时,您的DELETE将删除所有内容: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);
Run Code Online (Sandbox Code Playgroud)

这将删除端口表中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);
Run Code Online (Sandbox Code Playgroud)

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