这些交叉连接是否会给我带来问题?

时间:2009-07-01 16:41:22

标签: sql sql-server tsql cross-join

我有一个穷人的复制设置,我无法做任何事情。来自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

每晚清除队列表并重新开始。在调查这一点时,隐含的交叉连接向我跳了起来(我在他们通常是邪恶的一面),但后来我读了The power of the Cross Join。我在这里是因为我不太相信。假设临时表当天有大约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]))

2 个答案:

答案 0 :(得分:1)

您描述的查询不是CROSS JOIN

SQL Server非常聪明,可以将WHERE条件转换为JOIN

但是,我认为这里的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

如果不是,只需将DISTINCT添加到上面的SELECT

<强>更新

根据您的计划,我发现您的查询使用HASH JOIN,而我使用MERGE SEMI JOIN

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

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

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

确保在WHERE条件和SELECT子句中仅使用复合索引中的字段,如果可能,请将它们NOT NULL

这应该使您的查询在MERGE SEMI JOIN中使用预先排序的结果集。如果您在计划中看不到TABLE SCANSORT,只有两个INDEX SCAN,则可以告诉它。

还有两个问题:

  • c.id上的PRIMARY KEYcall_table吗?
  • q.id上的PRIMARY KEY#tmp吗?

如果对这两个问题的回答是yes,那么您将从做两件事中受益:

  • 在两个表格中将PRIMARY KEY定义为CLUSTERED
  • 重写您的查询:

     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
    

答案 1 :(得分:0)

  

这个查询如何运作?是否   它将两张桌子混合在一起   那么内存总计1,000,000,000   使用group子句将其修剪回来   下?你能解释一下SQL的步骤吗?   需要编译结果吗?

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

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

通过阅读执行计划,您可以看到Sql Server的真正功能。您可以在“查询”菜单下启用执行计划。