一些上下文:我有两个表,小表和bigtable。 Smalltable包含10,000行,而bigtable包含2,000,000,我使用的是SQL Server 2008.我从查询开始如下:
select * from [dbo].[smalltable] t1
INNER JOIN
[dbo].[bigtable] t2
on (t1.name1=t2.firstname and t1.name6=t2.lastname) or (t1.name6=t2.firstname and t1.name1=t2.lastname)
这个查询在我杀死它之前运行了超过15分钟 - 在检查查询计划时,它使用嵌套循环来进行内连接。
然后我重写了如下查询:
select * from [dbo].[smalltable] t1
INNER JOIN
[dbo].[bigtable] t2
on (t1.name1=t2.firstname and t1.name6=t2.lastname)
UNION
select * from [dbo].[smalltable] t1
INNER JOIN
[dbo].[bigtable] t2
on (t1.name6=t2.firstname and t1.name1=t2.lastname)
然后使用哈希匹配执行上面的两个查询,整个查询在4秒内运行。
我的问题是,为什么SQL Server得到的查询计划如此错误,无论如何我可以修复原始查询而不重写它?我尝试添加提示以对第一个查询使用哈希匹配,但似乎不允许您使用多个连接条件?
更新:根据请求添加了表中数据类型的示例。请注意,代码正在查找可能已交换名称的名称匹配项:
小表(列名1,名称6)
John, Smith
Johnny, Smith
Smythe, Jon
Michaels, Robert
Bob, Brown
Bigtable(列名字,姓氏)
John, Smith
John, Smythe
Johnny, Smith
Alison, Roberts
Robert, Michaels
Janet, Green
答案 0 :(得分:2)
这是SQL Server优化程序中的一个问题。
条件(t1.name1=t2.firstname and t1.name6=t2.lastname)
仅使用聚簇索引查找,因此非常快,即使使用非常大的表也几乎立即执行。
但条件是OR
(t1.name1=t2.firstname and t1.name6=t2.lastname) or (t1.name6=t2.firstname and t1.name1=t2.lastname)
查询优化器将始终执行表扫描或群集 如果查询中的WHERE子句包含OR,则对表进行索引扫描 运算符,如果OR子句中的任何引用列不是 索引(或没有有用的索引)。因此,如果你使用 使用OR子句的许多查询,您将需要确保每个 WHERE子句中的引用列具有索引。
如果您有一个使用OR的查询,但它没有得到最佳使用 索引,考虑将其重写为UNION,然后进行测试 性能。只有通过测试才能确定一个版本的 你的查询会比另一个更快。
See here。所以很快你的第一个OR查询就没有充分利用索引。
我相信除了用UNION(正如你所做的)或APPLY重写查询之外别无他法,优化器也不会这样做。