8

我有这个查询......运行速度非常慢(几乎一分钟):

select distinct main.PrimeId 
from PRIME main 
join   
( 
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId   or p.PrimeId = a.RelatedPrimeId    
where a.PrimeId is not null and a.RelatedPrimeId is not null  
) mem  
on main.PrimeId = mem.PrimeId

PRIME 表有 18k 行,并且在 PrimeId 上有 PK。

ATTRGROUP 表有 24k 行,并且在 PrimeId、col2、RelatedPrimeId 和 cols 4-7 上具有复合 PK。RelatedPrimeId 上还有一个单独的索引。

该查询最终返回 8.5k 行 - PRIME 表上与 ATTRGROUP 表上 PrimeId 或 RelatedPrimeId 匹配的不同 PrimeId 值

我有相同的查询,使用 ATTRADDRESS 而不是 ATTRGROUP。ATTRADDRESS 具有与 ATTRGROUP 相同的键和索引结构。它只有 11k 行,诚然,它更小,但在这种情况下,查询运行大约一秒钟,并返回 11k 行。

所以我的问题是:

尽管结构相同,但一个表上的查询怎么会比另一个慢得多。

到目前为止,我已经在 SQL 2005 和(使用相同的数据库,升级的)SQL 2008 R2 上尝试过这个。我们两个人独立获得了相同的结果,将相同的备份还原到两台不同的计算机上。

其他详情:

  • 括号内的位在不到一秒的时间内运行,即使在慢查询中也是如此
  • 执行计划中有一个可能的线索,我不明白。这是其中的一部分,有一个可疑的 320,000,000 行操作:

在此处输入图像描述 在此处输入图像描述

但是,该表上的实际行数略高于 24k,而不是 320M!

如果我重构括号内的查询部分,使其使用 UNION 而不是 OR,因此:

select distinct main.PrimeId 
from PRIME main 
join   
( 
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId
where a.PrimeId is not null and a.RelatedPrimeId is not null  
UNION
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.RelatedPrimeId    
where a.PrimeId is not null and a.RelatedPrimeId is not null  
) mem  
on main.PrimeId = mem.PrimeId

...然后慢查询需要不到一秒钟。

我非常感谢对此的任何见解!如果您需要更多信息,请告诉我,我会更新问题。谢谢!

顺便说一句,我意识到在这个例子中有一个冗余连接。这不能轻易删除,因为在生产中整个事物是动态生成的,并且括号中的位采用许多不同的形式。


编辑

我已经在 ATTRGROUP 上重建了索引,没有显着差异。

编辑 2

如果我使用临时表,则:

select distinct p.PrimeId into #temp
from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId   or p.PrimeId = a.RelatedPrimeId    
where a.PrimeId is not null and a.RelatedPrimeId is not null  

select distinct main.PrimeId 
from Prime main join   
#temp mem  
on main.PrimeId = mem.PrimeId

...再一次,即使在原始的 OUTER JOIN 中使用 OR,它也可以在不到一秒的时间内运行。我讨厌这样的临时表,因为它总是让人感觉像是在承认失败,所以这不是我将要使用的重构,但我认为它带来了如此不同的效果很有趣。

编辑 3

更新统计数据也没有区别。

感谢您迄今为止的所有建议。

4

4 回答 4

6

根据我的经验,最好在 JOIN 子句中使用两个左连接而不是 OR。所以而不是:

    left  outer join ATTRGROUP a 
    on p.PrimeId = a.PrimeId   or p.PrimeId = a.RelatedPrimeId

我会建议:

    left  outer join ATTRGROUP a 
    on p.PrimeId = a.PrimeId
    left  outer join ATTRGROUP a2
    on p.PrimeId = a2.RelatedPrimeId    
于 2011-08-12T08:51:33.773 回答
3

我注意到主查询与子查询不相关:

select distinct main.PrimeId 
from PRIME main 
join   
( 
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.PrimeId
where *main.PrimeId = a.PrimeId*  
UNION
select distinct p.PrimeId   from PRIME p   
left  outer join ATTRGROUP a 
on p.PrimeId = a.RelatedPrimeId    
where *main.PrimeId = a.PrimeId*  
) mem  
on main.PrimeId = mem.PrimeId

在这种构造中,您也不需要使用“is not null”子句(您是否需要它,因为主键永远不会包含空值?)。

我被教导要避免 OR 构造(正如其他人已经建议的那样),但也要避免“不为空”或“在valuelist中” - 构造。这些大多可以用(NOT)EXISTS子句代替。

于 2011-11-25T09:26:04.367 回答
1

这不是一个直接的答案,但如果您有从 ATTRGROUP.PrimeId 和 ATTRGROUP.RelatedPrimeId 到 main 的 FK 约束,那么您的查询相当于这个更简单的查询:

select PrimeId   from ATTRGROUP a 
union
select RelatedPrimeId from ATTRGROUP a 
于 2011-08-12T14:54:55.133 回答
0

一个表上的一个查询可能比另一个慢得多的一个原因是该表上的统计信息已过时,并且它选择了错误的查询计划。

但是,我支持摆脱其他人建议的 or 子句的重构。

于 2011-08-12T14:59:52.117 回答