Элегантный способ удалить строки, на которые нет ссылок в другой таблице - PullRequest
35 голосов
/ 22 июля 2010

У меня есть две таблицы (Tasks и Timeentries), которые связаны внешним ключом (TimeEntries.TaskID ссылается на Tasks.ID)

Теперь я хотел бы удалить все строки из Tasks, на которые нет ссылокпо таблице TimeEntries.Я подумал, что это должно сработать:

DELETE FROM Tasks WHERE ID not IN (SELECT TaskID FROM TimeEntries)

Но это влияет на 0 строк, даже если в таблице «Задачи» много строк без ссылок.

В чем здесь проблема?Конечно, я мог бы написать SP, который перебирает все строки, но кажется, что это можно сделать в один слой.

Полагаю, это одна из тех ошибок недосыпания во время сна.Пожалуйста, помогите!

Ответы [ 5 ]

51 голосов
/ 23 июля 2010

Есть одна пресловутая ошибка для not in.По сути, id not in (1,2,3) является сокращением для:

id <> 1 and id <> 2 and id <> 3

Теперь, если ваша таблица TimeEntries содержит какую-либо строку с TaskID из null, not in преобразуется в:

ID <> null and ID <> 1 and ID <> 2 AND ...

Результат сравнения с null всегда равен unknown.Поскольку unknown неверно в SQL, предложение where отфильтровывает все строки, и в итоге вы ничего не удаляете.

Простое исправление - это дополнительное условие where в подзапросе:

DELETE FROM Tasks 
WHERE  ID not IN 
       (
       SELECT  TaskID 
       FROM    TimeEntries 
       WHERE   TaskID is not null
       )
20 голосов
/ 23 июля 2010

С одной стороны, это решит проблему, с которой вы сталкиваетесь с пустыми значениями (для получения дополнительной информации см. Ссылку ниже)

DELETE FROM Tasks 
WHERE NOT EXISTS (SELECT 1 FROM TimeEntries 
                  WHERE TimeEntries.TaskID  = Tasks.ID )

Чтобы понять проблему, с которой вы столкнулись, взгляните на Выбрать все строки из одной таблицы, которых нет в другой таблице

9 голосов
/ 23 июля 2010

Поскольку вы используете SQL 2008, вы можете использовать отличный новый синтаксис слияния.

MERGE Tasks AS target
USING TimeEntries as Source ON (Target.TaskID=Source.TaskID)
WHEN NOT MATCHED BY Source THEN DELETE; 
4 голосов
/ 13 апреля 2016

Я знаю, что это старый, но мне интересно, почему никто не упомянул запрос на удаление, как описано здесь . Итак, для справки:

DELETE FROM Tasks
FROM Tasks LEFT OUTER JOIN
    TimeEntries ON TimeEntries.TaskID = Tasks.ID
WHERE TimeEntries.TaskID IS NULL;

Этот синтаксис не совместим с ISO, поэтому будет работать только для T-SQL.

3 голосов
/ 23 июля 2010
  Delete FROM Tasks 
       WHERE not Exists 
          (SELECT 'X' FROM TimeEntries where TimeEntries.TaskID  = Tasks.ID)

Вышеприведенный SQL должен удалить все строки из задач, где Task.ID не существует в таблице записей времени.Я бы запустил его в качестве оператора выбора сначала для проверки:)

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