使用索引加速ORDER BY子句

时间:2018-08-24 19:27:38

标签: sql sql-server tsql clustered-index non-clustered-index

我有一个带有ORDER BY子句的查询,由于该表有超过1100万行,所以查询很慢。

我可以通过在ORDER BY子句的列上添加聚簇索引来极大地提高它的速度。但是,软件会根据用户设置按不同的列对查询进行排序。然后您cannot add more than one clustered index to a table

我的问题是:非聚集索引可用于提高ORDER BY性能吗?还是关于聚簇索引有什么特别之处,这意味着我将无法对所有列进行快速排序?

注意:我已经发布了real query and execution plan online,但是还有其他我不想讨论的问题。我没有创建数据库或编写查询。即使没有IN子句,查询仍然非常缓慢。

3 个答案:

答案 0 :(得分:6)

非聚集索引绝对可以用于优化排序。索引本质上是二进制搜索树,这意味着它们包含按顺序排序的值。

但是,根据查询的不同,您可能将SQL Server置于一个难题之中。

如果您有一个包含1亿行的表,则查询将匹配其中的1100万行,如下所示,使用category上的索引来选择行并按{{1 }},还是从name预排序的索引中读取所有1亿行,然后通过检查name过滤掉其中的8900万行?

category

从理论上讲,SQL Server可以使用select ... from product where category = ? order by name; 上的索引来按顺序读取行,并且 使用name上的索引进行有效过滤?我很怀疑我很少见过SQL Server使用多个索引访问同一查询中的同一表(假设选择了一个表,而忽略了联接或递归CTE)。它必须检查该索引1亿次。索引每次索引搜索的开销成本很高,因此当单个搜索将结果集缩小很多时,索引效率很高。

在没有看到架构,统计信息和确切查询的情况下,很难说出什么有意义,但是我希望我会发现SQL Server将对where子句使用索引并对结果进行排序,而忽略了排序列。

如果您要选择整个表,则可以使用排序列上的索引。像category

同样,您的里程可能会有所不同。这是基于过去的经验进行的推测。

答案 1 :(得分:2)

我只有两美分。

另一个答案很好,但是没有解决删除一个唯一索引索引的想法。对于某些人来说,这个想法就像亵渎:D,但我已经看到了它的作用。

首先,当然,可以使用非聚集索引来加快搜索速度。

如果您的表是聚集索引表(在SQL Server中大多数是聚集索引表),则所有其他非聚集索引都将成为“二级”索引,因此效率不高。如果您的查询检索到几行(例如,少于1万行),您将不会真正注意到“二级索引效果”。当您开始看到此问题时,就是查询检索到许多行的时候。

为什么他们没有效率?因为所有二级索引都不指向“行ID”。它们不是因为聚集索引表中没有行ID。次要索引指向一个 key 。而且,这可能会变慢,具体取决于聚集索引的选择性。

完全不同的策略(至少值得出于基准测试的目的而考虑)是完全删除聚集索引。这样,所有行都将获得行ID,并且突然所有二级索引将成为真正的一级索引。如果我没记错的话,您需要将表重新创建为non-clustered;也许您可以修改它,但我不确定。

如果您的查询速度变慢,我认为值得考虑采用这种策略来加快所有二级索引的速度。

答案 2 :(得分:0)

关于@SeanLange关于索引是一门艺术而不是一门科学的评论,我所见过的最好的foo bar是表的所有列都在主键中。此外,如果您不小心,只是根据每个查询执行计划创建索引,则最终可能会在索引中存储比实际表更多的数据。

这里的想法是使用覆盖查询。对于您的情况,我已经看到了标识字段上的聚集索引,其中非聚集索引包含包含聚集索引列的主键(通常是复合主键)。从那里开始,SELECT基于聚簇索引(已排序)上的主键和顺序。

更新

我刚刚看到了查询执行计划。您将受到表扫描的打击,这意味着WHERE子句中的任何列都不包含在主键或索引中。就优化器而言,该表正在堆中运行。因此,您添加的任何包含(即覆盖)WHERE子句中包含的列的索引都可能被使用。结果,查询将返回得更快。

理想情况下,您希望看到索引搜索,然后是索引扫描。通常,优化器将通过其在索引中的顺序位置来查找唯一标识符。这意味着,如果身份列是索引中列出的第一列,则应该获得索引查找的奖励。如果索引中的第一列不唯一,那么您将获得索引扫描。我不会说这些是一成不变的规则,但这是基于我已阅读的文献和所见的执行计划而得出的理解。