SQL Server 2008+ 聚集索引的排序顺序
Sort order of an SQL Server 2008+ clustered index
SQL Server 2008+ 聚集索引的排序顺序是否影响插入性能?
特定情况下的数据类型是 integer
并且插入的值是升序的 (Identity
)。因此,索引的排序顺序将与要插入的值的排序顺序相反。
我的猜测是,它会产生影响,但我不知道,也许SQL服务器针对这种情况进行了一些优化,或者它的内部数据存储格式对此无动于衷。
请注意,问题是关于 INSERT
性能,而不是 SELECT
。
更新
更清楚地了解这个问题:当将要插入的值 (integer
) 的顺序 (ASC
) 与聚簇索引 (DESC
) 的顺序相反时会发生什么?
只要数据按聚簇索引排序(无论是升序还是降序),都不会对插入性能产生任何影响。这背后的原因是 SQL 不关心聚集索引页中行的物理顺序。行的顺序保存在所谓的 "Record Offset Array" 中,这是唯一需要为新行重写的行(不管顺序如何,无论如何都会完成)。实际的数据行将一个接一个地写入。
在事务日志级别,无论方向如何,条目都应该相同,因此这不会对性能产生任何额外影响。通常事务日志是产生大部分性能问题的日志,但在这种情况下会有 none.
您可以在此处找到有关页面/行的物理结构的很好解释https://www.simple-talk.com/sql/database-administration/sql-server-storage-internals-101/。
所以基本上只要您的插入不会产生页面拆分(如果数据按聚簇索引的顺序出现,无论顺序如何,它都不会),您的插入对插入性能的影响可以忽略不计.
有区别。乱序插入会导致大量碎片。
当您运行以下代码时,DESC 聚簇索引在 NONLEAF 级别生成额外的 UPDATE 操作。
CREATE TABLE dbo.TEST_ASC(ID INT IDENTITY(1,1)
,RandNo FLOAT
);
GO
CREATE CLUSTERED INDEX cidx ON dbo.TEST_ASC(ID ASC);
GO
CREATE TABLE dbo.TEST_DESC(ID INT IDENTITY(1,1)
,RandNo FLOAT
);
GO
CREATE CLUSTERED INDEX cidx ON dbo.TEST_DESC(ID DESC);
GO
INSERT INTO dbo.TEST_ASC VALUES(RAND());
GO 100000
INSERT INTO dbo.TEST_DESC VALUES(RAND());
GO 100000
这两个 Insert 语句产生完全相同的执行计划,但在查看操作统计数据时,差异显示 [nonleaf_update_count]。
SELECT
OBJECT_NAME(object_id)
,*
FROM sys.dm_db_index_operational_stats(DB_ID(),OBJECT_ID('TEST_ASC'),null,null)
UNION
SELECT
OBJECT_NAME(object_id)
,*
FROM sys.dm_db_index_operational_stats(DB_ID(),OBJECT_ID('TEST_DESC'),null,null)
当 SQL 使用针对 IDENTITY 的 运行 的 DESC 索引时,有一个额外的 - 在引擎盖下 - 操作正在进行。
这是因为 DESC table 变得支离破碎(行插入到页面的开头)并且会发生其他更新以维护 B 树结构。
关于此示例最值得注意的是 DESC 聚簇索引变得超过 99% 是碎片化的。 This is recreating the same bad behaviour as using a random GUID for a clustered index.
下面的代码演示了碎片。
SELECT
OBJECT_NAME(object_id)
,*
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('dbo.TEST_ASC'), NULL, NULL ,NULL)
UNION
SELECT
OBJECT_NAME(object_id)
,*
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('dbo.TEST_DESC'), NULL, NULL ,NULL)
更新:
在某些测试环境中,我还看到随着 [page_io_latch_wait_count] 和 [page_io_latch_wait_in_ms][=15= 的增加,DESC table 受到更多 WAITS 的影响]
更新:
当 SQL 可以执行向后扫描时,出现了一些关于降序索引的意义所在的讨论。请阅读这篇关于 limitations of Backward Scans 的文章。
根据下面的代码,当所选数据的排序方向与排序的聚集索引相反时,将数据插入具有排序聚集索引的标识列会消耗更多资源。
在此示例中,逻辑读取几乎翻了一番。
运行 10 次后,排序的升序逻辑读取平均为 2284,排序的降序逻辑读取平均为 4301。
--Drop Table Destination;
Create Table Destination (MyId INT IDENTITY(1,1))
Create Clustered Index ClIndex On Destination(MyId ASC)
set identity_insert destination on
Insert into Destination (MyId)
SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
ORDER BY n
set identity_insert destination on
Insert into Destination (MyId)
SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
ORDER BY n desc;
更多关于逻辑读取的信息,如果您有兴趣:
https://www.brentozar.com/archive/2012/06/tsql-measure-performance-improvements/
插入聚集索引的值的顺序肯定会影响索引的性能,因为它可能会产生大量碎片,并且还会影响插入本身的性能。
我构建了一个 test-bed 看看会发生什么:
USE tempdb;
CREATE TABLE dbo.TestSort
(
Sorted INT NOT NULL
CONSTRAINT PK_TestSort
PRIMARY KEY CLUSTERED
, SomeData VARCHAR(2048) NOT NULL
);
INSERT INTO dbo.TestSort (Sorted, SomeData)
VALUES (1797604285, CRYPT_GEN_RANDOM(1024))
, (1530768597, CRYPT_GEN_RANDOM(1024))
, (1274169954, CRYPT_GEN_RANDOM(1024))
, (-1972758125, CRYPT_GEN_RANDOM(1024))
, (1768931454, CRYPT_GEN_RANDOM(1024))
, (-1180422587, CRYPT_GEN_RANDOM(1024))
, (-1373873804, CRYPT_GEN_RANDOM(1024))
, (293442810, CRYPT_GEN_RANDOM(1024))
, (-2126229859, CRYPT_GEN_RANDOM(1024))
, (715871545, CRYPT_GEN_RANDOM(1024))
, (-1163940131, CRYPT_GEN_RANDOM(1024))
, (566332020, CRYPT_GEN_RANDOM(1024))
, (1880249597, CRYPT_GEN_RANDOM(1024))
, (-1213257849, CRYPT_GEN_RANDOM(1024))
, (-155893134, CRYPT_GEN_RANDOM(1024))
, (976883931, CRYPT_GEN_RANDOM(1024))
, (-1424958821, CRYPT_GEN_RANDOM(1024))
, (-279093766, CRYPT_GEN_RANDOM(1024))
, (-903956376, CRYPT_GEN_RANDOM(1024))
, (181119720, CRYPT_GEN_RANDOM(1024))
, (-422397654, CRYPT_GEN_RANDOM(1024))
, (-560438983, CRYPT_GEN_RANDOM(1024))
, (968519165, CRYPT_GEN_RANDOM(1024))
, (1820871210, CRYPT_GEN_RANDOM(1024))
, (-1348787729, CRYPT_GEN_RANDOM(1024))
, (-1869809700, CRYPT_GEN_RANDOM(1024))
, (423340320, CRYPT_GEN_RANDOM(1024))
, (125852107, CRYPT_GEN_RANDOM(1024))
, (-1690550622, CRYPT_GEN_RANDOM(1024))
, (570776311, CRYPT_GEN_RANDOM(1024))
, (2120766755, CRYPT_GEN_RANDOM(1024))
, (1123596784, CRYPT_GEN_RANDOM(1024))
, (496886282, CRYPT_GEN_RANDOM(1024))
, (-571192016, CRYPT_GEN_RANDOM(1024))
, (1036877128, CRYPT_GEN_RANDOM(1024))
, (1518056151, CRYPT_GEN_RANDOM(1024))
, (1617326587, CRYPT_GEN_RANDOM(1024))
, (410892484, CRYPT_GEN_RANDOM(1024))
, (1826927956, CRYPT_GEN_RANDOM(1024))
, (-1898916773, CRYPT_GEN_RANDOM(1024))
, (245592851, CRYPT_GEN_RANDOM(1024))
, (1826773413, CRYPT_GEN_RANDOM(1024))
, (1451000899, CRYPT_GEN_RANDOM(1024))
, (1234288293, CRYPT_GEN_RANDOM(1024))
, (1433618321, CRYPT_GEN_RANDOM(1024))
, (-1584291587, CRYPT_GEN_RANDOM(1024))
, (-554159323, CRYPT_GEN_RANDOM(1024))
, (-1478814392, CRYPT_GEN_RANDOM(1024))
, (1326124163, CRYPT_GEN_RANDOM(1024))
, (701812459, CRYPT_GEN_RANDOM(1024));
第一列是主键,如您所见,值以随机(大概)顺序列出。以随机顺序列出值应该使 SQL 服务器:
- 对数据进行排序,pre-insert
- 没有对数据进行排序,导致碎片化table。
函数CRYPT_GEN_RANDOM()
用于每行生成1024字节的随机数据,让这个table消耗多页,进而让我们看到分片插入的效果。
一旦你运行上面的插入,你可以像这样检查碎片:
SELECT *
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('TestSort'), 1, 0, 'SAMPLED') ips;
运行 我的 SQL Server 2012 Developer Edition 实例上的这个显示平均碎片率为 90%,表明 SQL 服务器在插入期间没有排序。
这个特殊故事的寓意可能是,“有疑问时,排序,如果它会有益”。话虽如此,向插入语句添加和 ORDER BY
子句并不能保证插入将按该顺序发生。例如,考虑如果插入平行会发生什么。
在 non-production 系统上,您可以使用跟踪标志 2332 作为插入语句的一个选项,以“强制”SQL 服务器在插入输入之前对输入进行排序。 @PaulWhite has an interesting article, Optimizing T-SQL queries that change data 包括那个和其他细节。请注意,跟踪标志不受支持,不应在生产系统中使用,因为这可能会使您的保修失效。在 non-production 系统中,为了您自己的教育,您可以尝试将此添加到 INSERT
语句的末尾:
OPTION (QUERYTRACEON 2332);
将其附加到插入后,查看计划,您会看到明确的排序:
如果 Microsoft 将其设为受支持的跟踪标志,那就太好了。
Paul White made me aware SQL 服务器 会 在认为排序运算符有用时自动将排序运算符引入计划。对于上面的示例查询,如果我 运行 在 values
子句中插入 250 个项目,则不会自动执行任何排序。但是,在 251 个项目中,SQL 服务器会在插入之前自动对值进行排序。为什么截断是 250/251 行对我来说仍然是个谜,除了它似乎是 hard-coded。如果我将 SomeData
列中插入的数据大小减少到仅一个字节,则截止值为 still 250/251 行,即使 [=92] 的大小=] 在这两种情况下都只是一个页面。有趣的是,查看带有 SET STATISTICS IO, TIME ON;
的插入显示带有单字节 SomeData
值的插入在排序时花费两倍的时间。
没有排序(即插入 250 行):
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time:
CPU time = 16 ms, elapsed time = 16 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Table 'TestSort'. Scan count 0, logical reads 501, physical reads 0,
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob
read-ahead reads 0.
(250 row(s) affected)
(1 row(s) affected)
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 11 ms.
随着排序(即插入 251 行):
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time:
CPU time = 15 ms, elapsed time = 17 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Table 'TestSort'. Scan count 0, logical reads 503, physical reads 0,
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob
read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0,
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob
read-ahead reads 0.
(251 row(s) affected)
(1 row(s) affected)
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 21 ms.
一旦开始增加行大小,排序版本肯定会变得更加高效。将 4096 字节插入 SomeData
时,排序插入在我的测试平台上的速度几乎是未排序插入的两倍。
作为 side-note,如果您有兴趣,我使用 T-SQL:
生成了 VALUES (...)
子句
;WITH s AS (
SELECT v.Item
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) v(Item)
)
, v AS (
SELECT Num = CONVERT(int, CRYPT_GEN_RANDOM(10), 0)
)
, o AS (
SELECT v.Num
, rn = ROW_NUMBER() OVER (PARTITION BY v.Num ORDER BY NEWID())
FROM s s1
CROSS JOIN s s2
CROSS JOIN s s3
CROSS JOIN v
)
SELECT TOP(50) ', ('
+ REPLACE(CONVERT(varchar(11), o.Num), '*', '0')
+ ', CRYPT_GEN_RANDOM(1024))'
FROM o
WHERE rn = 1
ORDER BY NEWID();
这会生成 1,000 个随机值,仅选择第一列中具有唯一值的前 50 行。我把copied-and-pasted输出到上面的INSERT
语句中。
SQL Server 2008+ 聚集索引的排序顺序是否影响插入性能?
特定情况下的数据类型是 integer
并且插入的值是升序的 (Identity
)。因此,索引的排序顺序将与要插入的值的排序顺序相反。
我的猜测是,它会产生影响,但我不知道,也许SQL服务器针对这种情况进行了一些优化,或者它的内部数据存储格式对此无动于衷。
请注意,问题是关于 INSERT
性能,而不是 SELECT
。
更新
更清楚地了解这个问题:当将要插入的值 (integer
) 的顺序 (ASC
) 与聚簇索引 (DESC
) 的顺序相反时会发生什么?
只要数据按聚簇索引排序(无论是升序还是降序),都不会对插入性能产生任何影响。这背后的原因是 SQL 不关心聚集索引页中行的物理顺序。行的顺序保存在所谓的 "Record Offset Array" 中,这是唯一需要为新行重写的行(不管顺序如何,无论如何都会完成)。实际的数据行将一个接一个地写入。
在事务日志级别,无论方向如何,条目都应该相同,因此这不会对性能产生任何额外影响。通常事务日志是产生大部分性能问题的日志,但在这种情况下会有 none.
您可以在此处找到有关页面/行的物理结构的很好解释https://www.simple-talk.com/sql/database-administration/sql-server-storage-internals-101/。
所以基本上只要您的插入不会产生页面拆分(如果数据按聚簇索引的顺序出现,无论顺序如何,它都不会),您的插入对插入性能的影响可以忽略不计.
有区别。乱序插入会导致大量碎片。
当您运行以下代码时,DESC 聚簇索引在 NONLEAF 级别生成额外的 UPDATE 操作。
CREATE TABLE dbo.TEST_ASC(ID INT IDENTITY(1,1)
,RandNo FLOAT
);
GO
CREATE CLUSTERED INDEX cidx ON dbo.TEST_ASC(ID ASC);
GO
CREATE TABLE dbo.TEST_DESC(ID INT IDENTITY(1,1)
,RandNo FLOAT
);
GO
CREATE CLUSTERED INDEX cidx ON dbo.TEST_DESC(ID DESC);
GO
INSERT INTO dbo.TEST_ASC VALUES(RAND());
GO 100000
INSERT INTO dbo.TEST_DESC VALUES(RAND());
GO 100000
这两个 Insert 语句产生完全相同的执行计划,但在查看操作统计数据时,差异显示 [nonleaf_update_count]。
SELECT
OBJECT_NAME(object_id)
,*
FROM sys.dm_db_index_operational_stats(DB_ID(),OBJECT_ID('TEST_ASC'),null,null)
UNION
SELECT
OBJECT_NAME(object_id)
,*
FROM sys.dm_db_index_operational_stats(DB_ID(),OBJECT_ID('TEST_DESC'),null,null)
当 SQL 使用针对 IDENTITY 的 运行 的 DESC 索引时,有一个额外的 - 在引擎盖下 - 操作正在进行。 这是因为 DESC table 变得支离破碎(行插入到页面的开头)并且会发生其他更新以维护 B 树结构。
关于此示例最值得注意的是 DESC 聚簇索引变得超过 99% 是碎片化的。 This is recreating the same bad behaviour as using a random GUID for a clustered index. 下面的代码演示了碎片。
SELECT
OBJECT_NAME(object_id)
,*
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('dbo.TEST_ASC'), NULL, NULL ,NULL)
UNION
SELECT
OBJECT_NAME(object_id)
,*
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('dbo.TEST_DESC'), NULL, NULL ,NULL)
更新:
在某些测试环境中,我还看到随着 [page_io_latch_wait_count] 和 [page_io_latch_wait_in_ms][=15= 的增加,DESC table 受到更多 WAITS 的影响]
更新:
当 SQL 可以执行向后扫描时,出现了一些关于降序索引的意义所在的讨论。请阅读这篇关于 limitations of Backward Scans 的文章。
根据下面的代码,当所选数据的排序方向与排序的聚集索引相反时,将数据插入具有排序聚集索引的标识列会消耗更多资源。
在此示例中,逻辑读取几乎翻了一番。
运行 10 次后,排序的升序逻辑读取平均为 2284,排序的降序逻辑读取平均为 4301。
--Drop Table Destination;
Create Table Destination (MyId INT IDENTITY(1,1))
Create Clustered Index ClIndex On Destination(MyId ASC)
set identity_insert destination on
Insert into Destination (MyId)
SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
ORDER BY n
set identity_insert destination on
Insert into Destination (MyId)
SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
ORDER BY n desc;
更多关于逻辑读取的信息,如果您有兴趣: https://www.brentozar.com/archive/2012/06/tsql-measure-performance-improvements/
插入聚集索引的值的顺序肯定会影响索引的性能,因为它可能会产生大量碎片,并且还会影响插入本身的性能。
我构建了一个 test-bed 看看会发生什么:
USE tempdb;
CREATE TABLE dbo.TestSort
(
Sorted INT NOT NULL
CONSTRAINT PK_TestSort
PRIMARY KEY CLUSTERED
, SomeData VARCHAR(2048) NOT NULL
);
INSERT INTO dbo.TestSort (Sorted, SomeData)
VALUES (1797604285, CRYPT_GEN_RANDOM(1024))
, (1530768597, CRYPT_GEN_RANDOM(1024))
, (1274169954, CRYPT_GEN_RANDOM(1024))
, (-1972758125, CRYPT_GEN_RANDOM(1024))
, (1768931454, CRYPT_GEN_RANDOM(1024))
, (-1180422587, CRYPT_GEN_RANDOM(1024))
, (-1373873804, CRYPT_GEN_RANDOM(1024))
, (293442810, CRYPT_GEN_RANDOM(1024))
, (-2126229859, CRYPT_GEN_RANDOM(1024))
, (715871545, CRYPT_GEN_RANDOM(1024))
, (-1163940131, CRYPT_GEN_RANDOM(1024))
, (566332020, CRYPT_GEN_RANDOM(1024))
, (1880249597, CRYPT_GEN_RANDOM(1024))
, (-1213257849, CRYPT_GEN_RANDOM(1024))
, (-155893134, CRYPT_GEN_RANDOM(1024))
, (976883931, CRYPT_GEN_RANDOM(1024))
, (-1424958821, CRYPT_GEN_RANDOM(1024))
, (-279093766, CRYPT_GEN_RANDOM(1024))
, (-903956376, CRYPT_GEN_RANDOM(1024))
, (181119720, CRYPT_GEN_RANDOM(1024))
, (-422397654, CRYPT_GEN_RANDOM(1024))
, (-560438983, CRYPT_GEN_RANDOM(1024))
, (968519165, CRYPT_GEN_RANDOM(1024))
, (1820871210, CRYPT_GEN_RANDOM(1024))
, (-1348787729, CRYPT_GEN_RANDOM(1024))
, (-1869809700, CRYPT_GEN_RANDOM(1024))
, (423340320, CRYPT_GEN_RANDOM(1024))
, (125852107, CRYPT_GEN_RANDOM(1024))
, (-1690550622, CRYPT_GEN_RANDOM(1024))
, (570776311, CRYPT_GEN_RANDOM(1024))
, (2120766755, CRYPT_GEN_RANDOM(1024))
, (1123596784, CRYPT_GEN_RANDOM(1024))
, (496886282, CRYPT_GEN_RANDOM(1024))
, (-571192016, CRYPT_GEN_RANDOM(1024))
, (1036877128, CRYPT_GEN_RANDOM(1024))
, (1518056151, CRYPT_GEN_RANDOM(1024))
, (1617326587, CRYPT_GEN_RANDOM(1024))
, (410892484, CRYPT_GEN_RANDOM(1024))
, (1826927956, CRYPT_GEN_RANDOM(1024))
, (-1898916773, CRYPT_GEN_RANDOM(1024))
, (245592851, CRYPT_GEN_RANDOM(1024))
, (1826773413, CRYPT_GEN_RANDOM(1024))
, (1451000899, CRYPT_GEN_RANDOM(1024))
, (1234288293, CRYPT_GEN_RANDOM(1024))
, (1433618321, CRYPT_GEN_RANDOM(1024))
, (-1584291587, CRYPT_GEN_RANDOM(1024))
, (-554159323, CRYPT_GEN_RANDOM(1024))
, (-1478814392, CRYPT_GEN_RANDOM(1024))
, (1326124163, CRYPT_GEN_RANDOM(1024))
, (701812459, CRYPT_GEN_RANDOM(1024));
第一列是主键,如您所见,值以随机(大概)顺序列出。以随机顺序列出值应该使 SQL 服务器:
- 对数据进行排序,pre-insert
- 没有对数据进行排序,导致碎片化table。
函数CRYPT_GEN_RANDOM()
用于每行生成1024字节的随机数据,让这个table消耗多页,进而让我们看到分片插入的效果。
一旦你运行上面的插入,你可以像这样检查碎片:
SELECT *
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('TestSort'), 1, 0, 'SAMPLED') ips;
运行 我的 SQL Server 2012 Developer Edition 实例上的这个显示平均碎片率为 90%,表明 SQL 服务器在插入期间没有排序。
这个特殊故事的寓意可能是,“有疑问时,排序,如果它会有益”。话虽如此,向插入语句添加和 ORDER BY
子句并不能保证插入将按该顺序发生。例如,考虑如果插入平行会发生什么。
在 non-production 系统上,您可以使用跟踪标志 2332 作为插入语句的一个选项,以“强制”SQL 服务器在插入输入之前对输入进行排序。 @PaulWhite has an interesting article, Optimizing T-SQL queries that change data 包括那个和其他细节。请注意,跟踪标志不受支持,不应在生产系统中使用,因为这可能会使您的保修失效。在 non-production 系统中,为了您自己的教育,您可以尝试将此添加到 INSERT
语句的末尾:
OPTION (QUERYTRACEON 2332);
将其附加到插入后,查看计划,您会看到明确的排序:
如果 Microsoft 将其设为受支持的跟踪标志,那就太好了。
Paul White made me aware SQL 服务器 会 在认为排序运算符有用时自动将排序运算符引入计划。对于上面的示例查询,如果我 运行 在 values
子句中插入 250 个项目,则不会自动执行任何排序。但是,在 251 个项目中,SQL 服务器会在插入之前自动对值进行排序。为什么截断是 250/251 行对我来说仍然是个谜,除了它似乎是 hard-coded。如果我将 SomeData
列中插入的数据大小减少到仅一个字节,则截止值为 still 250/251 行,即使 [=92] 的大小=] 在这两种情况下都只是一个页面。有趣的是,查看带有 SET STATISTICS IO, TIME ON;
的插入显示带有单字节 SomeData
值的插入在排序时花费两倍的时间。
没有排序(即插入 250 行):
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server parse and compile time: CPU time = 16 ms, elapsed time = 16 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. Table 'TestSort'. Scan count 0, logical reads 501, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. (250 row(s) affected) (1 row(s) affected) SQL Server Execution Times: CPU time = 0 ms, elapsed time = 11 ms.
随着排序(即插入 251 行):
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server parse and compile time: CPU time = 15 ms, elapsed time = 17 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. Table 'TestSort'. Scan count 0, logical reads 503, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. (251 row(s) affected) (1 row(s) affected) SQL Server Execution Times: CPU time = 16 ms, elapsed time = 21 ms.
一旦开始增加行大小,排序版本肯定会变得更加高效。将 4096 字节插入 SomeData
时,排序插入在我的测试平台上的速度几乎是未排序插入的两倍。
作为 side-note,如果您有兴趣,我使用 T-SQL:
生成了VALUES (...)
子句
;WITH s AS (
SELECT v.Item
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) v(Item)
)
, v AS (
SELECT Num = CONVERT(int, CRYPT_GEN_RANDOM(10), 0)
)
, o AS (
SELECT v.Num
, rn = ROW_NUMBER() OVER (PARTITION BY v.Num ORDER BY NEWID())
FROM s s1
CROSS JOIN s s2
CROSS JOIN s s3
CROSS JOIN v
)
SELECT TOP(50) ', ('
+ REPLACE(CONVERT(varchar(11), o.Num), '*', '0')
+ ', CRYPT_GEN_RANDOM(1024))'
FROM o
WHERE rn = 1
ORDER BY NEWID();
这会生成 1,000 个随机值,仅选择第一列中具有唯一值的前 50 行。我把copied-and-pasted输出到上面的INSERT
语句中。