我有一个穷人的复制设置,我无法做任何事情。来自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]))
答案 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.id
是PRIMARY KEY
。
如果不是,只需将DISTINCT
添加到上面的SELECT
。
<强>更新强>
根据您的计划,我发现您的查询使用HASH JOIN
,而我使用MERGE SEMI JOIN
。
如果您有一个有序集,后一个通常会更有效,但由于某种原因,查询不使用您创建的复合索引,而是执行全表扫描。
这很奇怪,因为你的所有值都包含在索引中。
可能(可能)这是因为您的字段允许NULL
。
确保在WHERE
条件和SELECT
子句中仅使用复合索引中的字段,如果可能,请将它们NOT NULL
。
这应该使您的查询在MERGE SEMI JOIN
中使用预先排序的结果集。如果您在计划中看不到TABLE SCAN
或SORT
,只有两个INDEX SCAN
,则可以告诉它。
还有两个问题:
c.id
上的PRIMARY KEY
是call_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的真正功能。您可以在“查询”菜单下启用执行计划。