RAND()
更新每行,然后从该表格中选择随机数列<0.1的行。如果可能的话,我正在寻找更简单的方法,在单个语句中完成。
这篇文章建议使用NEWID()
函数。这看起来很有前途,但我不知道如何可靠地选择一定比例的行。
有人以前做过这件事吗?有什么想法吗?
RAND()
更新每行,然后从该表格中选择随机数列<0.1的行。如果可能的话,我正在寻找更简单的方法,在单个语句中完成。
这篇文章建议使用NEWID()
函数。这看起来很有前途,但我不知道如何可靠地选择一定比例的行。
有人以前做过这件事吗?有什么想法吗?
select top 10 percent * from [yourtable] order by newid()
回应有关大型表格的“纯垃圾”评论:您可以像这样改进性能。
select * from [yourtable] where [yourPk] in
(select top 10 percent [yourPk] from [yourtable] order by newid())
这将会消耗掉键值扫描和连接的成本,但在选择百分比较小的大表上应该是合理的。newid()
进行排序,那么预估的I/O成本将会非常高,并影响性能。 - aadi1295TABLESAMPLE
可以帮助您获得几乎随机且性能更好的数据。该功能适用于 MS SQL Server 2005 及以上版本。
TABLESAMPLE
将从随机页面返回数据,而不是从随机行返回数据,因此它甚至不会检索它不会返回的数据。select top 1 percent * from [tablename] order by newid()
花费了超过20分钟的时间。
select * from [tablename] tablesample(1 percent)
花了2分钟。
TABLESAMPLE
对较小的样本表现也会有所提高,而使用newid()
则不会。
请记住,这种方法的随机性不如newid()
,但可以给你一个不错的样本。
请参阅MSDN页面。
使用newid() / order by可以工作,但对于大结果集来说会非常昂贵,因为它必须为每一行生成一个id,然后对它们进行排序。
TABLESAMPLE()在性能方面很好,但您将获得结果的聚类(页面上的所有行都将返回)。
对于更好的随机抽样性能,最好的方法是随机筛选行。我在SQL Server Books Online文章使用TABLESAMPLE限制结果集中找到了以下代码示例:
If you really want a random sample of individual rows, modify your query to filter out rows randomly, instead of using TABLESAMPLE. For example, the following query uses the NEWID function to return approximately one percent of the rows of the Sales.SalesOrderDetail table:
SELECT * FROM Sales.SalesOrderDetail WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float) / CAST (0x7fffffff AS int)
The SalesOrderID column is included in the CHECKSUM expression so that NEWID() evaluates once per row to achieve sampling on a per-row basis. The expression CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) evaluates to a random float value between 0 and 1.
当对包含1,000,000行的表进行运行时,以下是我的结果:
SET STATISTICS TIME ON
SET STATISTICS IO ON
/* newid()
rows returned: 10000
logical reads: 3359
CPU time: 3312 ms
elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()
/* TABLESAMPLE
rows returned: 9269 (varies)
logical reads: 32
CPU time: 0 ms
elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)
/* Filter
rows returned: 9994 (varies)
logical reads: 3359
CPU time: 641 ms
elapsed time: 627 ms
*/
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)
SET STATISTICS IO OFF
SET STATISTICS TIME OFF
NewID()
只被评估了一次,而不是每一行都评估,这让我不太喜欢... - Andrew Mao在MSDN上,选择大表的随机行有一个简单、清晰的解决方案,能够解决大规模性能问题。
SELECT * FROM Table1
WHERE (ABS(CAST(
(BINARY_CHECKSUM(*) *
RAND()) as int)) % 100) < 10
RAND()
不会为每一行返回相同的值(这将破坏 BINARY_CHECKSUM()
的逻辑)。是因为它被调用在另一个函数内而不是作为 SELECT 子句的一部分吗? - John M Gantrand()
或上述两者的组合造成的问题,但我因此放弃了这个解决方案。此外,结果数量从1到5不等,因此在某些情况下可能也不可接受。 - OliverRAND()
返回每一行相同的值(这就是为什么这个解决方案很快的原因)。然而,二进制校验和非常接近的行存在生成类似校验和结果的高风险,当 RAND()
很小时会导致聚集。例如,(ABS(CAST((BINARY_CHECKSUM(111,null,null) * 0.1) as int))) % 100
== SELECT (ABS(CAST((BINARY_CHECKSUM(113,null,null) * 0.1) as int))) % 100
。如果您的数据遇到此问题,请将 BINARY_CHECKSUM
乘以 9923。 - Brian这个链接提供了一个有趣的比较,比较了使用Orderby(NEWID())和其他方法在包含100万、700万和1300万行的表中进行随机选取行的效果。
常常在讨论组中有人问如何选择随机行,而NEWID查询通常会被提出来;它非常简单且对于小表格来说效果非常好。
SELECT TOP 10 PERCENT *
FROM Table1
ORDER BY NEWID()
然而,当你在大表中使用NEWID查询时,它有一个很大的缺点。ORDER BY子句会导致表中所有行被复制到tempdb数据库中进行排序。这会带来两个问题:
你需要的是一种选择随机行的方法,它不会使用tempdb,并且随着表变得越来越大,速度也不会变慢。以下是如何做到这一点的新想法:
SELECT * FROM Table1
WHERE (ABS(CAST(
(BINARY_CHECKSUM(*) *
RAND()) as int)) % 100) < 10
这个查询的基本思想是针对表中的每一行生成 0 到 99 之间的随机数,然后选择所有随机数小于指定百分比值的行。在这个例子中,我们希望随机选取约 10% 的行;因此,我们选择所有随机数小于 10 的行。通过随机数对表进行排序,并使用TOP
获取前5000行。
SELECT TOP 5000 * FROM [Table] ORDER BY newid();
更新
刚试了一下,调用newid()
即可 - 不需要进行所有强制类型转换和数学运算。
如果你(不同于提问者)需要特定数量的记录(这使得使用CHECKSUM方法很困难),并且希望比TABLESAMPLE本身提供的更随机的样本,并且想要比CHECKSUM更快的速度,那么可以将TABLESAMPLE和NEWID()方法结合起来,例如:
DECLARE @sampleCount int = 50
SET STATISTICS TIME ON
SELECT TOP (@sampleCount) *
FROM [yourtable] TABLESAMPLE(10 PERCENT)
ORDER BY NEWID()
SET STATISTICS TIME OFF
在我的情况下,这是最简单的平衡随机性(我知道实际上并不是)和速度的方法。根据需要变化 TABLESAMPLE 的百分比(或行数)- 百分比越高,样本就越随机,但速度会线性下降。(请注意,TABLESAMPLE 不接受变量)。SELECT TOP [number]
FROM table_name
ORDER BY RAND(CHECKSUM(*) * RAND())
在MySQL中,您可以这样做:
SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;
我还没有看到类似这样的答案变化。我有一个额外的限制,需要在给定初始种子的情况下,每次选择相同的一组行。
对于 MS SQL:
最小示例:
select top 10 percent *
from table_name
order by rand(checksum(*))
执行时间归一化: 1.00
NewId()示例:
select top 10 percent *
from table_name
order by newid()
标准化执行时间: 1.02
NewId()
的执行速度比 rand(checksum(*))
稍慢,因此可能不适用于大型记录集。
初始种子选择:
declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */
select top 10 percent *
from table_name
order by rand(checksum(*) % @seed) /* any other math function here */