【问题标题】:Trying to fix SQL query with two tables尝试使用两个表修复 SQL 查询
【发布时间】:2010-09-22 15:59:03
【问题描述】:

我有以下表格:

参赛作品
EntryID - int
EntryDate - 日期时间

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

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

参赛作品
条目ID:8
报名时间:9/9/2010 12:31:25

小时
条目ID:8
营业时间:2010 年 9 月 9 日 12:31:25
营业时间:2010 年 9 月 9 日 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 表,并将删除服务器上已经存在且没有相应 Hour 注册表且 OutHour 为空的任何注册表。但是今天我发现一条 Entry 记录被删除了,但相应的 Hour 没有(它的 OutHour 为空)。

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

谢谢!

【问题讨论】:

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


    【解决方案1】:

    问题在于您的第二个查询仅使用 InHour,而没有引用 EntryID。此外,您的第一个查询的条件彼此完全独立,如果您的 Hour 表约束正确(当第二列不为空时,第一列永远不能为空),这可能不是问题,但值得一看。

    在关系数据库中,最好养成用 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
    

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

    另外,请注意,链接服务器上的大量连接可能会导致性能非常差,因为有时查询引擎决定通过链接提取大量行集,甚至是整个表。您可以通过批量执行操作来缓解这种情况,可能是首先基于链接中的 JOIN 对临时表进行选择,然后以 100 或 1000 或 5000 的小批量删除相应的行(测试是为了找到正确的大小)。

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

    【讨论】:

      【解决方案2】:

      我的第一个建议是将两者之间的外键关系放在 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多