使用索引加速 ORDER BY 子句
Speeding ORDER BY clause with index
我有一个带有 ORDER BY
子句的查询,由于 table 有超过 1100 万行,所以查询速度很慢。
我可以通过在 ORDER BY
子句中的列上添加聚集索引来显着加快速度。但是,该软件会根据用户设置创建查询以按不同的列排序。还有你cannot add more than one clustered index to a table.
我的问题是:非聚集索引可以用来提高ORDER BY
性能吗?还是聚簇索引有什么特别之处,这意味着我将无法对所有列进行快速排序?
注意:我已经发布了我的 real query and execution plan online 但还有其他问题我不想在这里讨论。我没有创建数据库或编写查询。即使没有 IN
子句,查询仍然很慢。
非聚集索引绝对可以用来优化排序。索引本质上是二叉搜索树,这意味着它们包含按顺序排序的值。
但是,根据查询,您可能会让 SQL 服务器陷入困境。
如果你有一个包含 1 亿行的 table,你的查询将匹配其中的 1100 万行,如下所示,在 category
到 select 上使用索引是否更便宜行并按 name
对结果进行排序,或者从按 name
预先排序的索引中读取所有 1 亿行,然后通过检查 category
过滤掉其中的 8900 万行?
select ...
from product
where category = ?
order by name;
理论上,SQL 服务器可以使用 name
上的索引按顺序读取行 并且 使用 [=11 上的索引=] 有效过滤?我很怀疑。我很少看到 SQL 服务器使用多个索引来访问同一查询中的相同 table(假设单个 table selection,忽略连接或递归 CTE)。它必须检查索引 1 亿次。索引每次索引搜索的开销成本很高,因此当单个搜索将结果集缩小很多时,它们是有效的。
在没有看到模式、统计信息和精确查询的情况下,我很难说出什么是有意义的,但我希望我会发现 SQL 服务器会为 where 子句使用索引并对结果进行排序, 忽略排序列上的索引。
如果您要 select 整个 table,则可以使用排序列上的索引。喜欢select ... from product order by name;
同样,您的里程数可能会有所不同。这是根据过去的经验推测。
关于@SeanLange 关于索引是一门艺术而不是一门科学的评论,我见过的最好的 foo 栏是 table 的所有列都在主键中的位置。此外,如果您不小心,只是根据每个查询执行计划创建索引,您最终可能会在索引中存储比实际 table 上更多的数据。
这里的想法是使用覆盖查询。对于您的情况,我已经看到在标识字段上的聚集索引,其中非聚集索引包含包含聚集索引列的主键(通常是复合主键)。从那里,SELECT 基于主键和聚簇索引的顺序(它已经排序)。
更新:
我刚看到查询执行计划。您遇到了 table 扫描,这意味着 WHERE 子句中的 none 列包含在主键或索引中。就优化器而言,堆中的 table 是 运行。因此,您添加的任何包含(即覆盖)包含在 WHERE 子句中的列的索引都可能被使用。因此,查询将 return 快得多。
理想情况下,您希望看到 Index Seeks 之后是 Index Scans。通常,优化器会根据其在索引中的序号位置来查找唯一标识符。这意味着如果标识列是索引中列出的第一列,那么您应该得到索引查找的奖励。如果索引中的第一列不唯一,那么您将进行索引扫描。我不会说这些是硬性规定,但这是我根据我阅读的文献和我看到的执行计划的理解。
我想这个问题的最佳解决方案是
- 创建代理键作为集群索引
- 为您的查询创建复合索引
例如,您有
SELECT a,b,c
FROM tbl
WHERE x=?,y=?,z=?
ORDER BY j,k,l DESC
然后,您创建复合索引
INDEX xyz_jkl (x,y,z,j,k,l DESC)
这样,您就可以针对每个查询进行优化。
代理键对于此 table 之外的查询很重要。将其设为 AUTO_INCREMENT 字段也使 INSERT 更快。
还要记住主键(聚簇索引)总是包含在索引中。
我有一个带有 ORDER BY
子句的查询,由于 table 有超过 1100 万行,所以查询速度很慢。
我可以通过在 ORDER BY
子句中的列上添加聚集索引来显着加快速度。但是,该软件会根据用户设置创建查询以按不同的列排序。还有你cannot add more than one clustered index to a table.
我的问题是:非聚集索引可以用来提高ORDER BY
性能吗?还是聚簇索引有什么特别之处,这意味着我将无法对所有列进行快速排序?
注意:我已经发布了我的 real query and execution plan online 但还有其他问题我不想在这里讨论。我没有创建数据库或编写查询。即使没有 IN
子句,查询仍然很慢。
非聚集索引绝对可以用来优化排序。索引本质上是二叉搜索树,这意味着它们包含按顺序排序的值。
但是,根据查询,您可能会让 SQL 服务器陷入困境。
如果你有一个包含 1 亿行的 table,你的查询将匹配其中的 1100 万行,如下所示,在 category
到 select 上使用索引是否更便宜行并按 name
对结果进行排序,或者从按 name
预先排序的索引中读取所有 1 亿行,然后通过检查 category
过滤掉其中的 8900 万行?
select ...
from product
where category = ?
order by name;
理论上,SQL 服务器可以使用 name
上的索引按顺序读取行 并且 使用 [=11 上的索引=] 有效过滤?我很怀疑。我很少看到 SQL 服务器使用多个索引来访问同一查询中的相同 table(假设单个 table selection,忽略连接或递归 CTE)。它必须检查索引 1 亿次。索引每次索引搜索的开销成本很高,因此当单个搜索将结果集缩小很多时,它们是有效的。
在没有看到模式、统计信息和精确查询的情况下,我很难说出什么是有意义的,但我希望我会发现 SQL 服务器会为 where 子句使用索引并对结果进行排序, 忽略排序列上的索引。
如果您要 select 整个 table,则可以使用排序列上的索引。喜欢select ... from product order by name;
同样,您的里程数可能会有所不同。这是根据过去的经验推测。
关于@SeanLange 关于索引是一门艺术而不是一门科学的评论,我见过的最好的 foo 栏是 table 的所有列都在主键中的位置。此外,如果您不小心,只是根据每个查询执行计划创建索引,您最终可能会在索引中存储比实际 table 上更多的数据。
这里的想法是使用覆盖查询。对于您的情况,我已经看到在标识字段上的聚集索引,其中非聚集索引包含包含聚集索引列的主键(通常是复合主键)。从那里,SELECT 基于主键和聚簇索引的顺序(它已经排序)。
更新:
我刚看到查询执行计划。您遇到了 table 扫描,这意味着 WHERE 子句中的 none 列包含在主键或索引中。就优化器而言,堆中的 table 是 运行。因此,您添加的任何包含(即覆盖)包含在 WHERE 子句中的列的索引都可能被使用。因此,查询将 return 快得多。
理想情况下,您希望看到 Index Seeks 之后是 Index Scans。通常,优化器会根据其在索引中的序号位置来查找唯一标识符。这意味着如果标识列是索引中列出的第一列,那么您应该得到索引查找的奖励。如果索引中的第一列不唯一,那么您将进行索引扫描。我不会说这些是硬性规定,但这是我根据我阅读的文献和我看到的执行计划的理解。
我想这个问题的最佳解决方案是
- 创建代理键作为集群索引
- 为您的查询创建复合索引
例如,您有
SELECT a,b,c
FROM tbl
WHERE x=?,y=?,z=?
ORDER BY j,k,l DESC
然后,您创建复合索引
INDEX xyz_jkl (x,y,z,j,k,l DESC)
这样,您就可以针对每个查询进行优化。
代理键对于此 table 之外的查询很重要。将其设为 AUTO_INCREMENT 字段也使 INSERT 更快。
还要记住主键(聚簇索引)总是包含在索引中。