我有一个包含约50,000行的SQL Server表。我想随机选择大约5,000行。我想到了一种复杂的方法,用“随机数”列创建一个临时表,将我的表复制到该表中,遍历该临时表并用来更新每一行RAND()
,然后从该表中选择随机数列< 0.1。我正在寻找一种更简单的方法,如果可能的话,可以在一个语句中。
本文建议使用该NEWID()
功能。这看起来很有希望,但是我看不到如何可靠地选择一定百分比的行。
有人做过吗?有任何想法吗?
我有一个包含约50,000行的SQL Server表。我想随机选择大约5,000行。我想到了一种复杂的方法,用“随机数”列创建一个临时表,将我的表复制到该表中,遍历该临时表并用来更新每一行RAND()
,然后从该表中选择随机数列< 0.1。我正在寻找一种更简单的方法,如果可能的话,可以在一个语句中。
本文建议使用该NEWID()
功能。这看起来很有希望,但是我看不到如何可靠地选择一定百分比的行。
有人做过吗?有任何想法吗?
Answers:
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())
此操作的成本将是对值进行加键扫描,再加上联接成本,在较大的表上选择较小的百分比应该是合理的。
[yourPk]
指什么呢?编辑:Nvm,弄清楚了...主键。Durrr
newid()
排序估计I / O成本)将非常高并且会影响性能。
根据您的需求,TABLESAMPLE
将为您带来几乎一样的随机性和更好的性能。在MS SQL Server 2005和更高版本上可用。
TABLESAMPLE
将从随机页面而不是随机行返回数据,因此deos甚至不会检索不会返回的数据。
我在一张很大的桌子上测试了
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联机丛书文章“ 使用TABLESAMPLE限制结果集”中找到以下代码示例:
如果您确实希望随机获取单个行的样本,请修改查询以随机过滤掉行,而不是使用TABLESAMPLE。例如,以下查询使用NEWID函数返回Sales.SalesOrderDetail表的大约百分之一的行:
SELECT * FROM Sales.SalesOrderDetail WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float) / CAST (0x7fffffff AS int)
CHECKSUM表达式中包含SalesOrderID列,因此NEWID()每行评估一次,以实现每行采样。表达式CAST(CHECKSUM(NEWID(),SalesOrderID)&0x7fffffff AS float / CAST(0x7fffffff AS int)的计算结果为0到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
如果您可以避免使用TABLESAMPLE,它将为您提供最佳性能。否则,请使用newid()/ filter方法。如果结果集较大,则newid()/ order by应该是最后的选择。
NewID()
它只被评估一次,而不是每行评估一次,我不喜欢...
从 MSDN上的大型表中随机选择行有一个简单,明确表达的解决方案,可以解决大规模性能问题。
SELECT * FROM Table1
WHERE (ABS(CAST(
(BINARY_CHECKSUM(*) *
RAND()) as int)) % 100) < 10
RAND()
每一行都不返回相同的值(这会破坏BINARY_CHECKSUM()
逻辑)。是因为它是在另一个函数中调用而不是SELECT子句的一部分吗?
rand()
或上述问题的组合-但出于这个原因,我拒绝了此解决方案。而且结果的数量从1到5不等,因此在某些情况下这可能也不可接受。
RAND()
对于每行返回相同的值(这就是为什么这种解决方案很快的原因)。但是,具有非常接近的二进制校验和的行极有可能产生相似的校验和结果,当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。–
对于具有1、7和13百万行的表,此链接对Orderby(NEWID())和其他方法进行了有趣的比较。
通常,当在讨论组中询问有关如何选择随机行的问题时,就会提出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的所有行。
请阅读MSDN中的全文。
如果您(不同于OP)需要特定数量的记录(这使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将不接受变量)
尚未在答案中看到这种变化。我有一个额外的约束,需要给定初始种子,每次选择相同的行集。
对于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 */
如果需要给定种子选择同一组,这似乎可行。
尝试这个:
SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()
我在子查询中使用它,它在子查询中返回了相同的行
SELECT ID ,
( SELECT TOP 1
ImageURL
FROM SubTable
ORDER BY NEWID()
) AS ImageURL,
GETUTCDATE() ,
1
FROM Mytable
然后我解决了在其中包含父表变量的问题
SELECT ID ,
( SELECT TOP 1
ImageURL
FROM SubTable
Where Mytable.ID>0
ORDER BY NEWID()
) AS ImageURL,
GETUTCDATE() ,
1
FROM Mytable
注意哪里有条件
没有指定使用中的服务器端处理语言(例如PHP,.net等),但是如果是PHP,则获取所需的数字(或所有记录),而不是使用PHP的shuffle函数在查询中进行随机化。我不知道.net是否具有等效功能,但是如果您使用.net则使用该功能
ORDER BY RAND()可能会影响性能,具体取决于所涉及的记录数量。
这对我有用:
SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]
select top 10 percent from table_name order by rand()
,但这也不起作用,因为rand()在所有行上都返回相同的值。