【问题标题】:Why is one query extremely slow, yet identical query on similar table runs in the blink of an eye为什么一个查询非常慢,但在相似表上的相同查询却在眨眼间运行
【发布时间】:2011-10-25 14:41:39
【问题描述】:

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

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 表上的 PrimeId 的不同值与 ATTRGROUP 表上的 PrimeId 或 RelatedPrimeId 匹配

我有相同的查询,使用 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:

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

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

【问题讨论】:

  • @Ruirize:你为什么这么说?
  • 18K * 24K 为 432M。在 320M 的范围内。可能是巧合,但可能是一个值得一看的地方。
  • 你是说你的复合主键是 6 列宽,并且聚集索引在 PrimaryKey 上?
  • 如果ATTRGROUP 上的PrimeId 确实是表的主键,它就不能为空,那么为什么这个条件会出现在你的内部WHERE 子句中?跨度>
  • @Chris - 是的,但是您的WHERE 子句将结果集限制为a.PrimeId 不为空的结果(例如,左连接成功,a.PrimeId 的值不会是null),或者a.RelatedPrimeId不为null(如左连接成功,a.PrimeId的值不会为null)。如果您的左连接受到WHERE 的限制以始终成功,则它实际上是一个内连接。

标签: sql-server query-performance


【解决方案1】:

根据我的经验,在 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    

【讨论】:

  • 是的,这也是我的经验。您在这里的重构确实运行得非常快,实际上比我的 UNION 快一点。但是,我的问题并不是关于如何重构原始查询(尽管我显然需要这样做!!),这就是为什么它在一个表上非常慢,但在其他类似大小和相同索引结构的表上却没有明显慢.
  • @ChrisA,数据的构成同样重要。例如,如果在更快的基于 ATTRADDRESS 的查询中 RelatedPrimeId 始终为 null,则 SQL 将优化查询的低效 OR 部分。
【解决方案2】:

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

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-constructions(正如其他人已经建议的那样),但也要避免'is not null'或'in valuelist' - 构造。这些大多可以用(NOT)EXISTS 子句替换。

【讨论】:

    【解决方案3】:

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

    select PrimeId   from ATTRGROUP a 
    union
    select RelatedPrimeId from ATTRGROUP a 
    

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 感谢您的建议。我现在已经更新了统计数据,但同样没有区别/
      猜你喜欢
      • 1970-01-01
      • 2018-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-05
      • 2011-10-19
      相关资源
      最近更新 更多