【发布时间】: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