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

Jos*_*ody 132 sql sql-server indexing optimization

当您在MS SQL Server(我使用的是版本2005)中的列或列数上创建索引时,您可以指定每列上的索引是升序还是降序.我很难理解为什么这个选择就在这里.使用二进制排序技术,任何一种查找都不会那么快吗?它选择哪个订单有什么区别?

Qua*_*noi 129

这在与复合索引一起使用时非常重要:

CREATE INDEX ix_index ON mytable (col1, col2 DESC);
Run Code Online (Sandbox Code Playgroud)

可以用于:

SELECT  *
FROM    mytable
ORDER BY
        col1, col2 DESC
Run Code Online (Sandbox Code Playgroud)

要么:

SELECT  *
FROM    mytable
ORDER BY
        col1 DESC, col2
Run Code Online (Sandbox Code Playgroud)

,但不适用于:

SELECT  *
FROM    mytable
ORDER BY
        col1, col2
Run Code Online (Sandbox Code Playgroud)

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

有关详细信息,请参阅我博客中的文章:

更新:

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

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

CREATE TABLE mytable (
       pk INT NOT NULL PRIMARY KEY,
       col1 INT NOT NULL
)
CREATE INDEX ix_mytable_col1 ON mytable (col1)
Run Code Online (Sandbox Code Playgroud)

索引on col1保持有序值col1以及对行的引用.

由于表是聚簇的,因此对行的引用实际上是对的值pk.它们也在每个值内排序col1.

这意味着索引的叶子实际上是按顺序排序的(col1, pk),并且此查询:

SELECT  col1, pk
FROM    mytable
ORDER BY
        col1, pk
Run Code Online (Sandbox Code Playgroud)

不需要排序.

如果我们创建索引如下:

CREATE INDEX ix_mytable_col1_desc ON mytable (col1 DESC)
Run Code Online (Sandbox Code Playgroud)

,然后col1将按降序排序值,但pk每个值内的值col1将按升序排序.

这意味着以下查询:

SELECT  col1, pk
FROM    mytable
ORDER BY
        col1, pk DESC
Run Code Online (Sandbox Code Playgroud)

可以服务ix_mytable_col1_desc但不能服务ix_mytable_col1.

换句话说,构成CLUSTERED INDEX任何表上的列始终是该表上任何其他索引的尾随列.

  • 我的意思是索引不会用于查询.当然,查询本身会起作用,但性能会很差. (5认同)

Mar*_*ith 68

对于真正的单列索引,它与查询优化器的观点差别不大.

对于表定义

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

查询

SELECT TOP 10 *
FROM T1
ORDER BY ID DESC
Run Code Online (Sandbox Code Playgroud)

使用具有扫描方向的有序扫描,BACKWARD如执行计划中所示.但是,目前只有FORWARD扫描可以并行化.

计划

但是,它在逻辑碎片方面可以产生很大的不同.如果使用降序键创建索引但新行附加了升序键值,那么您最终可能会出现逻辑顺序中的每个页面.扫描表时,这会严重影响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
Run Code Online (Sandbox Code Playgroud)

对于下面的脚本

/*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 
Run Code Online (Sandbox Code Playgroud)

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

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 %% )
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


Mic*_*ren 8

当您想要检索大量已排序数据而非单个记录时,排序顺序很重要.

请注意(正如您对问题所建议的那样)排序顺序通常远不如您正在索引的列那么重要(如果顺序与其想要的顺序相反,系统可以反向读取索引).我很少考虑索引排序顺序,而我对索引所涵盖的列感到痛苦.

@Quassnoi提供了一个很好的例子,当它的事.