为什么在SQL Server中的索引列上执行top(1)会变慢?

时间:2010-03-16 11:04:18

标签: sql-server tsql query-optimization performance

我对以下内容感到困惑。我有一个大约1000万行的数据库,并且(在其他索引中)1列(campaignid_int)是一个索引。

现在我有700k行,其中campaignid确实是3835

对于所有这些行,connectionid是相同的。

我只是想找出这个连接。

 use messaging_db;
 SELECT     TOP (1) connectionid
 FROM         outgoing_messages WITH (NOLOCK)
 WHERE     (campaignid_int = 3835)

现在这个查询需要大约30秒才能完成!

我(使用我的小数据库知识)会期望它会占用任何行,并返回给我那个connectionid

如果我为只有1个条目的广告系列测试同样的查询,那么它的速度非常快。因此索引有效。

我如何解决这个问题?为什么这不起作用?

编辑:

estimated execution plan:

select (0%) - top (0%) - clustered index scan (100%)

8 个答案:

答案 0 :(得分:17)

由于统计信息,您应该明确要求优化器使用您创建的索引而不是群集索引。

SELECT  TOP (1) connectionid
FROM    outgoing_messages WITH (NOLOCK, index(idx_connectionid))
WHERE  (campaignid_int = 3835)

我希望它能解决问题。

此致 恩里克

答案 1 :(得分:9)

我最近遇到了同样的问题,解决起来非常简单(至少在某些情况下)。

如果在索引的任何或部分列上添加ORDER BY - 子句,则应解决该子句。这至少解决了我的问题。

答案 2 :(得分:4)

您没有在查询中指定ORDER BY子句,因此没有指示优化器应该从中选择前1的排序顺序。 SQL Server不仅会采用随机行,它会按行排序并占据前1位,它可能会选择按次优的顺序排序。我建议您添加一个ORDER BY x子句,其中x作为该表上的聚簇键可能是最快的。

这可能无法解决你的问题 - 实际上我不确定我是否会从你给出的统计数据中得到它 - 但是(a)它不会受到伤害,并且(b)你将能够把它排除在外作为一个促成因素。

答案 3 :(得分:1)

如果campaignid_int列未编入索引,请为其添加索引。这应该加快查询速度。现在我假设你需要进行全表扫描,以便在返回campaignid_int = 3835行之前找到top(1)的匹配项(在返回结果之前进行过滤)。

编辑:索引已经到位,但由于SQL Server执行了聚簇索引扫描,因此优化程序忽略了索引。这可能是由于(许多)重复的行具有相同的campaignid_int值。您应该考虑采用不同的索引或在不同的列上进行查询以获得所需的connectionid

答案 4 :(得分:1)

索引可能没用,原因有两个:

  • 1000万700k可能不够有选择性
  • 和/或
  • 需要包含connectionid,因此整个查询只能使用索引

否则,优化器决定它也可以使用PK /聚集索引来过滤campaignid_int并获取connectionid,以避免在当前索引的700k行上进行书签查找。

所以,我建议这个......

CREATE NONCLUSTERED INDEX IX_Foo ON MyTable (campaignid_int) INCLUDE (connectionid)

答案 5 :(得分:0)

您的查询无法正常工作,因为Sql Server会保留有关您的索引的统计信息,并且在此特定情况下知道有许多重复行的标识符为3835,因此它认为它更有意义做一个完整的索引(或表)扫描。当您测试只解析为一行的ID时,它会按预期使用索引,即执行索引查找(执行计划应验证此猜测)。

可能的解决方案?如果您有任何要编写的索引,请使索引合成,例如,用消息发送的日期(如果我理解你的情况正确)组成它,然后从列表中选择具有按日期排序的指定id的前1个条目。虽然我不确定这是否会更好(对于一个,复合索引占用更多空间) - 只是一个猜测。

编辑:我刚刚尝试了通过添加日期列来制作索引合成的建议。如果您这样做并在查询中指定order by date,则会按预期执行索引搜索。

答案 6 :(得分:0)

这不能解答您的问题,但请尝试使用:

SET ROWCOUNT 1
SELECT     connectionid
 FROM         outgoing_messages WITH (NOLOCK)
 WHERE     (campaignid_int = 3835)

我看到top(x)在某些情况下表现非常糟糕。我确定它正在进行全表扫描。也许您需要重建该特定列的索引?不过,上述情况值得一试。

答案 7 :(得分:0)

  

但是因为我指的是'top(1)'   意思是:给我任何一排。为什么会这样   首先爬过700k行   返回一个? - 30分钟前加速

很抱歉,还不能发表评论,但这里的答案是,当SQL服务器听到“Top 1”时,它不会理解人类相当于“把我找到的第一个”。而不是预期的“给我任何行”SQL Server去取所有找到的行中的第一个。 只有在知道这是在首先获取所有行之后,然后丢弃其余的时间。非常彻底,但在你的情况下不是很快。

正如其他所说的主要问题是您的索引的统计数据和选择性。如果表中有另一个唯一字段(如标识列),则首先尝试使用campaignid_int上的组合索引,然后尝试使用唯一列。由于您只对campaignid_int进行查询,因此它必须是密钥的第一部分。 听起来值得一试,因为这个索引应该具有更高的选择性,因此优化器可以比使用索引爬行更好地使用它。