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


138

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


Answers:


136

与复合索引一起使用时,这主要很重要:

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)

索引on 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在任何表上构成a的列始终是该表上任何其他索引的尾随列。


1
当您说“不适合……”时,您是说它不起作用,还是性能会很糟糕?
尼尔N

5
我的意思是索引将不会用于查询。当然,查询本身可以工作,但是性能会很差。
Quassnoi

1
在第一部分中,第二个示例不应该说“ ORDER BY col1 DESC,col2 DESC”吗?
米奇小麦

71

对于真正的单列索引,它与查询优化器的观点几乎没有什么不同。

对于表定义

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并行扫描。

计划

但是,就逻辑碎片而言,它可以带来很大的不同。如果索引是用降序排列的键创建的,但是新行会附加升序的键值,那么最后的每一页都可能不符合逻辑顺序。扫描表时,它会严重影响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 %% )

在此处输入图片说明


谢谢Martin的出色提示,这真的帮助我进行了排名查询
TheGameiswar 2015年

我想知道是否有一个降序索引,然后从mytable中选择mycolumn,其中,当\ @myvalue接近最大可能值时,indexed_column = \ @myvalue会比\ @myvalue接近最小可能值时要快。
Lajos Arpad

@LajosArpad为什么会更快?B树是平衡树。两者的树深相同。
马丁·史密斯

@MartinSmith的深度是一样的,但我怀疑兄弟姐妹的顺序不会有所不同
Lajos Arpad

@MartinSmith,如果兄弟姐妹的顺序甚至在性能上有细微的差别,那么运行数百万个选择将加起来,更不用说多维联接了。
Lajos Arpad

8

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

请注意(正如您所提的问题),排序顺序通常远不如您要索引的列重要(如果顺序与所需的顺序相反,则系统可以反向读取索引)。我很少给索引排序顺序任何想法,而我为索引所涵盖的列感到烦恼。

@Quassnoi提供了一个很好的例子,当它没有关系。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.