GUARANTEES列上的聚簇索引根据该列返回已排序的行

Mud*_*san 4 sql sql-server

在这个有争议的问题上,我无法得到明确的答案. MSDN文档提到

集群

  • 聚簇索引根据键值对表或视图中的数据行进行排序和存储.这些是索引定义中包含的列.每个表只能有一个聚簇索引,因为数据行本身只能一个顺序排序.

  • 表中的数据行以排序顺序存储的唯一时间是表包含聚簇索引.当表具有聚簇索引时,该表称为聚簇表.如果表没有聚簇索引,则其数据行存储在称为堆的无序结构中.

虽然我看到了大部分答案

回答否定.

它是什么 ?

Gor*_*off 7

只是为了清楚.据推测,您正在谈论一个简单的查询,例如:

select *
from table t;
Run Code Online (Sandbox Code Playgroud)

首先,如果表中的所有数据都适合单个页面并且表上没有其他索引,那么我很难想象一个结果集未按主键排序的情况.但是,这是因为我认为最合理的查询计划需要全表扫描,而不是因为SQL或SQL Server中的任何要求(记录或其他).如果没有显式order by,则结果集中的排序是查询计划的结果.

这是问题的核心.当您谈论结果集的排序时,您实际上是在谈论查询计划.而且,主键排序的假设实际上意味着您假设查询使用全表扫描.具有讽刺意味的是,人们在没有真正理解"为什么"的情况下做出了假设.此外,人们倾向于从小例子中概括(好吧,这是人类智能基础的一部分).不幸的是,他们始终认为,对小型表的简单查询的结果集总是按主键顺序排列,并推广到较大的表.在此示例中,归纳步骤不正确.

有什么可以改变这个?另外,我认为如果满足以下条件,全表扫描将以主键顺序返回数据:

  • 单线程服务器.
  • 单个文件组
  • 没有竞争指数
  • 没有表分区

我不是说这总是如此.在这些情况下,这样的查询将使用从表开始处开始的全表扫描,这似乎是合理的.

即使在小桌子上,你也会感到意外.考虑:

select NonPrimaryKeyColumn
from table
Run Code Online (Sandbox Code Playgroud)

查询计划可能会决定使用索引table(NonPrimaryKeyColumn)而不是进行全表扫描.主键不会对结果进行排序(除非偶然).我表明,因为索引可以用于多种目的,不只是这个例子order bywhere过滤.

如果您使用数据库的多线程实例并且具有合理大小的表,order by则无需明确排序即可快速了解结果.

最后,SQL Server有一个非常聪明的优化器.我认为order by在查询中有一些不情愿,因为用户认为它会自动进行排序.SQL Server很难找到查询的最佳执行计划.如果它order by因为计划的其余部分而认识到这是多余的,那么这order by将不会导致排序.

当然,您希望保证结果的排序,您需要order by在最外层的查询中.甚至像这样的查询:

select *
from (select top 100 t.* from t order by col1) t
Run Code Online (Sandbox Code Playgroud)

不保证结果在最终结果集中排序.你真的需要这样做:

select *
from (select top 100 t.* from t order by col1) t
order by col1;
Run Code Online (Sandbox Code Playgroud)

以特定顺序保证结果.这种行为记录在这里.


Nul*_*ull 6

如果没有ORDER BY,即使您有聚簇索引,也没有默认的排序顺序

这个链接中有一个很好的例子:

CREATE SCHEMA Data AUTHORIZATION dbo 
GO 
CREATE TABLE Data.Numbers(Number INT NOT NULL PRIMARY KEY) 
GO 
DECLARE @ID INT; 
SET NOCOUNT ON; 
SET @ID = 1; 
WHILE @ID < 100000 BEGIN 
 INSERT INTO Data.Numbers(Number) 
 SELECT @ID; 
 SET @ID = @ID+1; 
END 

CREATE TABLE Data.WideTable(ID INT NOT NULL  
CONSTRAINT PK_WideTable PRIMARY KEY, 
RandomInt INT NOT NULL, 
CHARFiller CHAR(1000)) 
GO 
CREATE VIEW dbo.WrappedRand 
AS 
SELECT RAND() AS random_value 
GO 
CREATE ALTER FUNCTION dbo.RandomInt() 
RETURNS INT 
AS 
BEGIN 
DECLARE @ret INT; 
SET @ret = (SELECT random_value*1000000 FROM dbo.WrappedRand); 
RETURN @ret; 
END 
GO 

INSERT INTO Data.WideTable(ID,RandomInt,CHARFiller) 
SELECT Number, dbo.RandomInt(), 'asdf' 
FROM Data.Numbers 
GO 
CREATE INDEX WideTable_RandomInt ON Data.WideTable(RandomInt) 
GO 
SELECT TOP 100 ID FROM Data.WideTable 
Run Code Online (Sandbox Code Playgroud)

OUTPUT:

1407
253
9175
6568
4506
1623
581
Run Code Online (Sandbox Code Playgroud)

如您所见,优化器已选择使用非聚集索引来满足此SELECT TOP查询.

显然,除非明确使用ORDER BY子句,否则不能假定您的结果是有序的.


归档时间:

查看次数:

2645 次

最近记录:

11 年 前