尝试使用两个表修复SQL查询

时间:2010-09-22 15:59:03

标签: sql sql-server sql-server-2005 tsql

我有以下表格:

条目
EntryID - int
EntryDate - datetime

小时
EntryID - int
InHour - 日期时间
OutHour - 日期时间

对于Entry表中的每个注册表,Hour表上应该至少有一个(可能很多)注册表,如下所示:

条目
EntryID:8
EntryDate:9/9/2010 12:31:25

小时
EntryID:8
InHour:9/9/2010 12:31:25
OutHour:9/9/2010 18:21:19

现在,此信息存储在2个相同的数据库中,一个在本地计算机上,一个在服务器上。我正在尝试编写一个查询,该查询将删除已经传递给服务器的所有信息,条件是不会删除没有OutHour(null)的注册表。

我写了以下查询:

DELETE from [dbo].[Entry]
 WHERE [dbo].[Entry].[EntryID] IN (SELECT [EntryID] 
                                     FROM [LINKEDSERVER].[MYDATABASE].[dbo].[Entry]) 
  AND [dbo].[Entry].[EntryID] IN (SELECT [EntryID] 
                                    FROM [dbo].[Hour] 
                                   WHERE [OutHour] IS NOT NULL)

DELETE from [dbo].[Hour]
 WHERE [dbo].[Hour].[InHour] IN (SELECT [InHour] 
                                   FROM [LINKEDSERVER].[MYDATABASE].[dbo].[Hour]) 
   AND [dbo].[Hour].[OutHour] IS NOT NULL 

AFAIK,此查询首先检查Entry表,并将删除已在服务器上的任何注册表,并且没有具有null OutHour的相应Hour注册表。然而今天我发现一个条目记录被删除但相应的小时没有(它有一个空的OutHour)。

我做错了什么?任何帮助表示赞赏。

谢谢!

2 个答案:

答案 0 :(得分:1)

出现问题的是,您的第二个查询仅使用InHour,而不引用EntryID。此外,您的第一个查询的条件完全相互独立,如果您的小时表约束正确(第一列在第二列不为空时永远不会为空),这可能不是问题,但值得关注。< / p>

在关系数据库中,最好习惯用JOIN而不是IN()来思考。使用IN()通常可以返回与JOIN相同的结果(在NULL处理方面存在一些差异),并且通常甚至可以获得相同的执行计划,但是#1是一种“放松”的方式来思考不会出借的问题本身对编写复杂查询所需的心理空间很好,#2不能同时比较多个值,它只能进行一次比较(至少在SQL Server中,因为其他一些DBMS可以这样做)。

让我将您的查询重写为JOIN,也许它会帮助您查看错误。

DELETE E
FROM
   dbo.Entry E
   INNER JOIN LINKEDSERVER.MYDATABASE.dbo.Entry L ON E.EntryID = L.EntryID
   INNER JOIN Hour H ON E.EntryID = H.EntryID
WHERE
   H.OutHour IS NOT NULL

DELETE H
FROM
   dbo.Hour H
   INNER JOIN LINKEDSERVER.MYDATABASE.dbo.Hour L ON H.InHour L.InHour
WHERE
   H.OutHour IS NOT NULL

我建议你在小时表上放置一个级联删除外键约束,这样当你从Entry表中删除时,子Hour行全部消失。这里仍然存在问题,因为每个EntryID可能有很多小时行,从语义上讲,您最终可能会尝试多次删除链接服务器上的同一行。

此外,请注意,链接服务器上的巨大连接可能会遇到非常差的性能,因为有时查询引擎会决定在链接上提取大量行集,甚至是整个表。您可以通过批量处理来缓解这种情况,可能首先根据链接上的JOIN选择一个临时表,然后删除100或1000或5000小批量的相应行(测试是为了找到正确的尺寸)。

最后,如果您确实发现您的查询不必要地通过链接提取大量数据(通过在远程matchine上运行Query Profiler来确定这一点以查看提交的实际查询),那么战略性地使用CROSS APPLY可以通过强制逐行处理来提供帮助,在链接服务器的情况下可以实现巨大的性能改进,尽管与标准和强烈建议相比,从不做行的反直觉 - 关系数据库中的行。可以把它想象成一个“拉伸书签查找”而不是“拉伸表扫描”,你就会明白为什么这会有如此大的帮助。

答案 1 :(得分:0)

我的第一个建议是在EntryID上放置两者之间的外键关系。如果没有先从Hour表中删除所有实例,这将阻止Entry表中的任何删除。

其次,使用外键,您必须从子项到父项(也就是从层次结构的底部开始)。这意味着我会先做这件事:

delete from dbo.Hour where OutHour is not null
delete e
from dbo.Entry e
left outer join dbo.Hour h
on e.entryid=h.entryid
where h.entryid is null