SQL Server 索引 - 升序或降序,有什么区别?

问题描述 投票:0回答:4

当您在 MS SQL Server 中的一列或多列上创建索引(我使用的是 2005 版本)时,您可以指定每列上的索引是升序还是降序。我很难理解为什么会有这样的选择。使用二进制排序技术,查找速度不是一样快吗?我选择哪种顺序有什么区别?

sql sql-server optimization indexing
4个回答
162
投票

这在与复合索引一起使用时尤为重要:

CREATE INDEX ix_index ON mytable (col1, col2 DESC);

可用于:

SELECT  *
FROM    mytable
ORDER BY
        col1, col2 DESC

或:

SELECT  *
FROM    mytable
ORDER BY
        col1 DESC, col2

,但不适用于:

SELECT  *
FROM    mytable
ORDER BY
        col1, col2

单列上的索引可以有效地用于两种方式的排序。

详情请参阅我的博客文章:

更新:

事实上,即使对于单列索引,这也很重要,尽管不是那么明显。

想象一下聚集表的列上的索引:

CREATE TABLE mytable (
       pk INT NOT NULL PRIMARY KEY,
       col1 INT NOT NULL
)
CREATE INDEX ix_mytable_col1 ON mytable (col1)

col1
上的索引保留
col1
的有序值以及对行的引用。

由于表是聚集的,所以对行的引用实际上是

pk
的值。它们也在
col1
的每个值内排序。

这意味着索引的叶子实际上是在

(col1, pk)
上排序的,并且这个查询:

SELECT  col1, pk
FROM    mytable
ORDER BY
        col1, pk

无需排序。

如果我们按如下方式创建索引:

CREATE INDEX ix_mytable_col1_desc ON mytable (col1 DESC)

,则

col1
的值将按降序排序,但
pk
的每个值中的
col1
的值将按升序排序。

这意味着以下查询:

SELECT  col1, pk
FROM    mytable
ORDER BY
        col1, pk DESC

可以由

ix_mytable_col1_desc
提供,但不能由
ix_mytable_col1
提供。

换句话说,任何表上构成

CLUSTERED INDEX
的列始终是该表上任何其他索引的尾随列。


82
投票

对于真正的单列索引,从查询优化器的角度来看,它没有什么区别。

对于表定义

CREATE TABLE T1( [ID] [int] IDENTITY NOT NULL,
                 [Filler] [char](8000) NULL,
                 PRIMARY KEY CLUSTERED ([ID] ASC))

查询

SELECT TOP 10 *
FROM T1
ORDER BY ID DESC

使用扫描方向为

BACKWARD
的有序扫描,如执行计划中所示。然而,有一点细微的差别,目前只有
FORWARD
扫描可以并行化。

Plan

但是它可以在逻辑碎片方面产生很大的影响。如果索引是使用降序键创建的,但新行附加了升序键值,那么最终可能会导致每个页面不符合逻辑顺序。这会严重影响扫描表时 IO 读取的大小,并且该表不在缓存中。

查看碎片结果

                    avg_fragmentation                    avg_fragment
name   page_count   _in_percent         fragment_count   _size_in_pages
------ ------------ ------------------- ---------------- ---------------
T1     1000         0.4                 5                200
T2     1000         99.9                1000             1

对于下面的脚本

/*Uses T1 definition from above*/
SET NOCOUNT ON;

CREATE TABLE T2( [ID] [int] IDENTITY NOT NULL,
                 [Filler] [char](8000) NULL,
                 PRIMARY KEY CLUSTERED ([ID] DESC))

BEGIN TRAN

GO
INSERT INTO T1 DEFAULT VALUES
GO 1000
INSERT INTO T2 DEFAULT VALUES
GO 1000

COMMIT

SELECT object_name(object_id) AS name, 
       page_count, 
       avg_fragmentation_in_percent, 
       fragment_count, 
       avg_fragment_size_in_pages 
FROM 
sys.dm_db_index_physical_stats(db_id(), object_id('T1'), 1, NULL, 'DETAILED') 
WHERE  index_level = 0 
UNION ALL 
SELECT object_name(object_id) AS name, 
       page_count, 
       avg_fragmentation_in_percent, 
       fragment_count, 
       avg_fragment_size_in_pages 
FROM 
sys.dm_db_index_physical_stats(db_id(), object_id('T2'), 1, NULL, 'DETAILED') 
WHERE  index_level = 0 

可以使用空间结果选项卡来验证这一假设:这是因为后面的页面在这两种情况下都具有升序键值。

SELECT page_id,
       [ID],
       geometry::Point(page_id, [ID], 0).STBuffer(4)
FROM   T1
       CROSS APPLY sys.fn_PhysLocCracker( %% physloc %% )
UNION ALL
SELECT page_id,
       [ID],
       geometry::Point(page_id, [ID], 0).STBuffer(4)
FROM   T2
       CROSS APPLY sys.fn_PhysLocCracker( %% physloc %% )

enter image description here


9
投票

当您想要检索大量排序数据而不是单个记录时,排序顺序很重要。

请注意(正如您在问题中所建议的那样)排序顺序通常远不如您正在索引的列重要(如果顺序与其想要的相反,系统可以反向读取索引)。我很少考虑索引排序顺序,而我却为索引所覆盖的列而苦恼。

@Quassnoi 提供了一个很好的例子,说明它何时确实很重要。


0
投票

可视化为什么

ASC
DSC
对于综合指数很重要

复合索引只是按字典顺序按列对行进行排序。

因此,

ASC
+
ASC
索引如下:

CREATE INDEX myindex ON mytable (a ASC, b ASC);

将排序行,例如:

a, b

1, 2
1, 3
1, 5
1, 6

2, 1
2, 2
2, 3
2, 5
2, 7

3, 1
3, 3
3, 4

同时:

CREATE INDEX myindex ON mytable (a ASC, b DESC);

将排序行,例如:

a, b

1, 6
1, 5
1, 3
1, 2

2, 7
2, 5
2, 3
2, 2
2, 1

3, 4
3, 3
3, 1

因此,很明显

(a ASC, b ASC)
索引可以有效地加速两者:

SELECT * FROM mytable ORDER BY a ASC,  b ASC
SELECT * FROM mytable ORDER BY a DESC, b DESC

并且

(a ASC, b DESC)
索引可以有效地加速:

SELECT * FROM mytable ORDER BY a ASC,  b DESC
SELECT * FROM mytable ORDER BY a DESC, b ASC

因为在那些高效的情况下,RDMS 可以:

  • 仅将起点平分一次
  • 然后只需线性遵循索引顺序即可,这是高效的

例如:

  • 要使用
    ORDER BY a ASC, b ASC
    索引执行
    (ASC, ASC)
    查询,我们将:
    • 向下平分到起点
      1, 2
    • 然后就往下索引
  • 要使用
    ORDER BY a DESC, b DESC
    索引执行
    (ASC, ASC)
    查询,我们将:
    • 平分到终点
      3, 4
    • 然后就上索引

但是这些都不适用于

(ASC, DESC)
索引,反之亦然。

© www.soinside.com 2019 - 2024. All rights reserved.