Tsql 按性能随机排序
按随机顺序获取前n行的最佳方法是什么?Tsql 按性能随机排序,tsql,Tsql,按随机顺序获取前n行的最佳方法是什么? 我使用如下查询: Select top(10) field1,field2 .. fieldn from Table1 order by checksum(newid()) 上面的查询中的问题是,随着表大小的增加,它将继续变慢。它将始终执行完整的聚集索引扫描,以按随机顺序查找top(10)行 还有其他更好的方法吗?没有,这里没有办法提高性能。因为您希望行按“随机”顺序排列,所以索引将是无用的。但是,您可以尝试通过newid()而不是其校验和进行排序,但这
我使用如下查询:
Select top(10) field1,field2 .. fieldn
from Table1
order by checksum(newid())
上面的查询中的问题是,随着表大小的增加,它将继续变慢。它将始终执行完整的聚集索引扫描,以按随机顺序查找top(10)
行
还有其他更好的方法吗?没有,这里没有办法提高性能。因为您希望行按“随机”顺序排列,所以索引将是无用的。但是,您可以尝试通过
newid()
而不是其校验和进行排序,但这只是对随机排序的优化,而不是排序本身
服务器无法知道您想要从表中随机选择10行。查询将为表中的每一行计算
order by
表达式,因为它是一个不能由索引值确定的计算值。这就是您看到完整聚集索引扫描的原因。我已经对此进行了测试,并在更改查询时获得了更好的性能
我在测试中使用的表的DDL
CREATE TABLE [dbo].[TestTable]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Col1] [nvarchar](100) NOT NULL,
[Col2] [nvarchar](38) NOT NULL,
[Col3] [datetime] NULL,
[Col4] [nvarchar](50) NULL,
[Col5] [int] NULL,
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED
(
[ID] ASC
)
)
GO
CREATE NONCLUSTERED INDEX [IX_TestTable_Col5] ON [dbo].[TestTable]
(
[Col5] ASC
)
该表有722888行
第一次查询:
select top 10
T.ID,
T.Col1,
T.Col2,
T.Col3,
T.Col5,
T.Col5
from TestTable as T
order by newid()
select
T.ID,
T.Col1,
T.Col2,
T.Col3,
T.Col5,
T.Col5
from TestTable as T
inner join (select top 10 ID
from TestTable
order by newid()) as C
on T.ID = C.ID
第一次查询的统计信息:
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 13 ms.
(10 row(s) affected)
Table 'TestTable'. Scan count 1, logical reads 12492, physical reads 14, read-ahead reads 6437, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 859 ms, elapsed time = 1700 ms.
执行计划第一次查询:
第二次查询:
select top 10
T.ID,
T.Col1,
T.Col2,
T.Col3,
T.Col5,
T.Col5
from TestTable as T
order by newid()
select
T.ID,
T.Col1,
T.Col2,
T.Col3,
T.Col5,
T.Col5
from TestTable as T
inner join (select top 10 ID
from TestTable
order by newid()) as C
on T.ID = C.ID
第二次查询的统计信息:
SQL Server parse and compile time:
CPU time = 125 ms, elapsed time = 183 ms.
(10 row(s) affected)
Table 'TestTable'. Scan count 1, logical reads 1291, physical reads 10, read-ahead reads 399, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 516 ms, elapsed time = 706 ms.
执行计划第二次查询:
摘要:
select top 10
T.ID,
T.Col1,
T.Col2,
T.Col3,
T.Col5,
T.Col5
from TestTable as T
order by newid()
select
T.ID,
T.Col1,
T.Col2,
T.Col3,
T.Col5,
T.Col5
from TestTable as T
inner join (select top 10 ID
from TestTable
order by newid()) as C
on T.ID = C.ID
第二个查询使用Col5
上的索引按newid()
对行进行排序,然后执行聚集索引搜索10次,以获取输出值
性能提高是因为Col5
上的索引比聚集键窄,导致读取次数更少
感谢for.减少必要扫描大小的一种方法是使用TABLESAMPLE和ORDER by newid的组合,以便从表中的页面选择随机数目的行,而不是扫描整个表 其思想是计算每页的平均行数,然后使用tablesample为要输出的每一行随机选择一页数据。然后,您将仅对该数据子集运行orderbynewid()查询。与原始方法相比,这种方法的随机性要小一些,但比仅使用tablesample要好得多,而且需要从表中读取的数据要少得多 不幸的是,TABLESAMPLE子句不接受变量,因此需要动态sql才能根据输入表记录的大小使用动态行值
declare @factor int
select @factor=8000/avg_record_size_in_bytes from sys.dm_db_index_physical_stats(db_id(), object_id('sample'), null, null, 'detailed') where index_level = 0
declare @numRows int = 10
declare @sampledRows int = @factor * @numRows
declare @stmt nvarchar(max) = N'select top (@numRows) * from sample tablesample (' + convert(varchar(32), @sampledRows) + ' rows) order by checksum(newid())'
exec sp_executesql @stmt, N'@numRows int', @numRows
这个问题有7年历史了,没有公认的答案。但当我搜索选择随机行的SQL性能时,它的排名很高。但是,对于大型表格,目前的答案似乎都不能提供简单、快速的解决方案,因此我想补充我的建议 假设:
- 主键是数字数据类型(每行典型的int/+1)
- 主键是聚集索引
- 该表有许多行,只应选择少数行
--
--首先,创建一个表,其中包含一些要从中选择的虚拟数据
--
如果存在主表,则删除该表
创建表MainTable(
Id int标识(1,1)不为空,
[名称]nvarchar(50)空,
[内容]文本为空
)
去
声明@I INT=0
而@I<40
开始
插入主表值('Foo','bar')
设置@I=@I+1
结束
更新主表集合[Name]=[Name]+CAST(Id为nvarchar(50))
--在末端的ID中创建间隙
从主表中删除
其中ID<10
——在中间创建一个IDS的空隙
从主表中删除
其中ID>=20且ID<30
--我们现在有了我们想要从中随机选择行的“源”数据
--
--然后我们从表中选择随机数据
--
--获取要从中拾取随机值的值的间隔
声明@MaxId int
从MainTable中选择@MaxId=MAX(Id)
声明@MinId int
从MainTable中选择@MinId=MIN(Id)
声明@RandomId int
声明@NumberOfIdsTofind int=10
--创建临时表以从中插入ID
删除表格(如果存在)#ID
创建表#Id(Id int)
而(@NumberOfIdsTofind>0)
开始
设置@RandomId=ROUND(((@MaxId-@MinId-1)*RAND()+@MinId),0)
--验证随机ID是否为主表中的真实ID
如果存在(从MainTable中选择Id,其中Id=@RandomId)
开始
--确认尚未插入随机ID
如果不存在(从#Id中选择Id,其中Id=@RandomId)
开始
--这是一个有效的新ID,请将其添加到列表中。
插入到#Ids值(@RandomId)
设置@NumberOfIdsTofind=@NumberOfIdsTofind-1;
结束
结束
结束
--通过将主表与随机ID连接,选择随机数据行
从MainTable中选择MainTable.*
内部联接#Ids ON#Ids.Id=MainTable.Id
为什么要按校验和(newid())而不是按newid()排序?如果要向每一行添加一个值(并且计算order by
子句中的函数会隐式地这样做),则需要先处理每一行,然后才能执行排序。我现在在想是否有办法避免给每一行分配排序值。。。如果每一行都包含一个唯一的int值,那么这可能是可行的——不同的解决方案取决于int值是连续的还是不连续的。这是我的猜测,没有办法避免完全索引扫描,以获得百万行表中的前10行。这意味着获取rand中的大量行