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

时间:2011-06-08 21:08:20

标签: 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;

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

update匹配时,一切都按预期工作。


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

2 个答案:

答案 0 :(得分: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);

请注意,子查询与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);

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);

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

答案 1 :(得分: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);

这将删除端口表中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值列表中的所有行。