pl / sql DELETE удаляет все строки вместо выбранных - PullRequest
4 голосов
/ 09 июня 2011

У меня есть триггер:

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 ]

8 голосов
/ 09 июня 2011

Ваш 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 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 все равно, что если какие-либо столбцы будут возвращены его подзапросом;только ли строки возвращены вообще.

5 голосов
/ 09 июня 2011

Ваш DELETE удаляет все, когда поле 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, возвращаемых в списке выбора.

Вы уверены, что ваше обновление работает правильно? Мне кажется, вы должны вести себя с ним одинаково - все строки обновлены.

EDIT:

Так как ваше обновление не проходит таким же образом, я предлагаю изменить его на:

  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, возвращаемых в списке выбора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...