删除未被其他表引用的行的优雅方法

时间:2010-07-22 19:57:47

标签: sql-server tsql sql-server-2008

我有两个表(Tasks和Timeentries),它们通过外键连接(TimeEntries.TaskID引用Tasks.ID)

现在,我想删除任务中未被TimeEntries表引用的所有行。我认为这应该有效:

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

但它会影响0行,即使Tasks表中有很多未引用的行。

这可能是什么问题?当然我可以写一个迭代所有行的SP,但看起来这可以在一个衬里完成。

我猜这是睡眠时间下溢错误之一。请帮忙!

5 个答案:

答案 0 :(得分:51)

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
       )

答案 1 :(得分:20)

一种方法,这将解决您使用空值的“问题”(请参阅​​下面的链接了解更多信息)

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

要了解您遇到的问题,请查看Select all rows from one table that don't exist in another table

答案 2 :(得分:9)

由于您运行的是SQL 2008,因此可以使用漂亮的新合并语法。

MERGE Tasks AS target
USING TimeEntries as Source ON (Target.TaskID=Source.TaskID)
WHEN NOT MATCHED BY Source THEN DELETE; 

答案 3 :(得分:4)

我知道这是旧的,但我想知道为什么没有人提到here描述的删除查询。所以,供参考:

n

此语法与ISO不兼容,因此仅适用于T-SQL。

答案 4 :(得分:3)

  Delete FROM Tasks 
       WHERE not Exists 
          (SELECT 'X' FROM TimeEntries where TimeEntries.TaskID  = Tasks.ID)

SQL Above应删除任务中所有行,其中Task.ID在时间条目表中不存在。我会首先将它作为select语句运行来测试:)