1

我有一个穷人的复制设置,我无能为力。来自 a 的一些标识数据(基本上是主键)call_table通过简单的触发器复制到另一个表中,然后“复制服务器”运行存储过程将数据从队列表复制到 #temp 表(以防止 SQL 中的锁定) 6.5是给我的情况)。call_table最后,查询使用临时表中的关键数据将数据从使用此查询拉回复制服务器:

  /* select the data to return to poor man replication server */     
  SELECT c.id,
         c.date,
         c.time,
         c.duration,
         c.location
    FROM #tmp q, call_table c (NOLOCK)
   WHERE q.id=c.id  
     AND q.date=c.date
     AND q.time=c.time
     AND q.duration=c.duration
     AND q.location=c.location
GROUP BY c.id,
         c.date,
         c.time,
         c.duration,
         c.location

每天晚上清理一次队列表,然后重新开始。在对此进行调查时,隐式交叉连接向我扑来(我站在他们通常是邪恶的一边),但后来我读到了交叉连接的力量。我在这里是因为我不太相信。假设 temp 表一天有大约 10,000 行,call_table 到目前为止一个月有大约 100,000 行。这个查询将如何工作?它是否将这两个表混合在一起,总共有 1,000,000,000 个内存,然后使用 group 子句将其修剪下来?你能解释一下 SQL 编译结果的步骤吗?

执行计划:

My Query:
      |--Hash Match Root(Aggregate, HASH:([c].[id], [c].[date], [c].[location], [c].[time], [c].[duration]), RESIDUAL:(((((((((((((((((((((([c].[id]=[c].[id] AND [c].[PIN]=[c].[PIN]) AND [c].[ORIG]=[c].[ORIG]) AND [c].[date]=[c].[date]) AND [c].[CTIME]=[c].[CTIME
           |--Hash Match Team(Inner Join, HASH:([q].[id], [q].[date], [q].[location], [q].[time], [q].[duration])=([c].[id], [c].[date], [c].[location], [c].[time], [c].[duration]), RESIDUAL:(((([c].[id]=[q].[id] AND [c].[location]=[q].[location]) AND [c].[duration]=[q].[duration]) AND [
                |--Table Scan(OBJECT:([db].[dbo].[queue] AS [q]))
                |--Table Scan(OBJECT:([db].[dbo].[call_table] AS [c]))

Yours:              
  |--Merge Join(Right Semi Join, MERGE:([q].[id], [q].[date], [q].[time], [q].[duration], [q].[location])=([c].[id], [c].[date], [c].[time], [c].[duration], [c].[location]), RESIDUAL:(((([q].[id]=[c].[id] AND [q].[location]=[c].[location]) AND [q].[duration]=[c].[duration]) AND [q].[
       |--Index Scan(OBJECT:([db].[dbo].[queue].[PK_queue] AS [q]), ORDERED)
       |--Sort(ORDER BY:([c].[id] ASC, [c].[date] ASC, [c].[time] ASC, [c].[duration] ASC, [c].[location] ASC))
            |--Table Scan(OBJECT:([db].[dbo].[call_table] AS [c]))
4

2 回答 2

1

您描述的查询绝不是CROSS JOIN.

SQL Server足够聪明,可以将WHERE条件转换为JOIN's.

但是,我认为GROUP BY这里没有意义。

这个查询:

  SELECT c.id,
         c.date,
         c.time,
         c.duration,
         c.location
    FROM #tmp q, call_table c (NOLOCK)
   WHERE q.id=c.id  
     AND q.date=c.date
     AND q.time=c.time
     AND q.duration=c.duration
     AND q.location=c.location
GROUP BY c.id,
         c.date,
         c.time,
         c.duration,
         c.location

可以很容易地改写为

  SELECT  c.id,
          c.date,
          c.time,
          c.duration,
          c.location
  FROM    call_table c (NOLOCK)
  WHERE   EXISTS
          (
          SELECT  NULL
          FROM    #tmp q
          WHERE   q.id = c.id  
                  AND q.date = c.date
                  AND q.time = c.time
                  AND q.duration = c.duration
                  AND q.location = c.location
          )

, 前提c.idPRIMARY KEY.

如果不是,只需添加DISTINCTSELECT上面。

更新:

从您的计划中,我看到您的查询使用HASH JOIN,而我的使用MERGE SEMI JOIN

如果您有一个有序集,后者通常更有效,但由于某种原因,查询不使用您创建的复合索引,而是执行全表扫描。

这很奇怪,因为您的所有值都包含在索引中。

可能(可能)这是因为您的字段允许NULL's.

确保在WHERE条件和SELECT子句中都只使用复合索引中的字段,如果可能的话,将它们设为NOT NULL.

这应该使您的查询使用MERGE SEMI JOIN. 如果您既没有看到也TABLE SCAN没有SORT在计划中看到,您可以告诉它,只有两个INDEX SCAN

还有两个问题:

  • c.idPRIMARY KEYcall_table
  • q.idPRIMARY KEY#tmp

如果这两个问题的答案都是yes,那么您将受益于做两件事:

  • 在两个表上定义你的PRIMARY KEYasCLUSTERED
  • 将您的查询重写为:

     SELECT  c.id,
             c.date,
             c.time,
             c.duration,
             c.location
     FROM    call_table c (NOLOCK)
     JOIN    #tmp q
     ON      q.id = c.id  
             AND q.date = c.date
             AND q.time = c.time
             AND q.duration = c.duration
             AND q.location = c.location
    
于 2009-07-01T16:46:10.417 回答
0

这个查询将如何工作?它是否将这两个表混合在一起,总共有 1,000,000,000 个内存,然后使用 group 子句将其修剪下来?你能解释一下 SQL 编译结果的步骤吗?

它可能会变成这样。假设 Sql Server 决定使用散列连接。它创建一个#temp 的内存哈希表,哈希表基于id、日期、时间、持续时间和位置。然后它遍历 call_table 中的行。对于每一行,它使用哈希表来检测是否存在匹配的行。如果是,则该行将添加到结果表中。因此,内存中永远不会有 1,000,000,000 行。

另一种选择(这里可能更好)是遍历一个表,并使用 id 列在另一个表上进行索引查找。这需要更少的内存(尽管如果索引在缓存中会非常有益。)

你可以通过阅读执行计划来了解 Sql Server 的真正作用。您可以在查询菜单下启用执行计划。

于 2009-07-01T17:11:15.067 回答