从SQL Server表中selectn个随机行
我有一个约50000行的SQL Server表。 我想随机select大约5000行。 我想到了一个复杂的方法,用“随机数”列创build临时表,将表复制到那个表中,循环遍历临时表,并用RAND()
更新每一行,然后从该表中select随机数列<0.1。 我正在寻找一个简单的方法来做到这一点,如果可能的话,在一个单一的声明。
本文build议使用NEWID()
函数。 这看起来很有希望,但我看不出我如何可靠地select一定比例的行。
有人曾经这样做过? 有任何想法吗?
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())
这个成本将成为价值加上联合成本的关键扫描,在一个大的表上select一个小百分比的select应该是合理的。
根据您的需求, TABLESAMPLE
将使您获得几乎随机和更好的性能。 这在MS SQL Server 2005和更高版本中可用。
TABLESAMPLE
将从随机页面而不是随机行返回数据,因此,甚至不会检索不会返回的数据。
在我testing的一个非常大的桌子上
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,然后对它们进行sorting。
从性能的angular度来看,TABLESAMPLE()是很好的,但是你会得到结果的聚集(页面上的所有行将被返回)。
为了更好地执行真正的随机样本,最好的方法是随机过滤行。 我在SQL Server联机丛书文章使用TABLESAMPLE限制结果集中find以下代码示例:
如果您确实需要单个行的随机样本,请修改您的查询以随机筛选出行,而不是使用TABLESAMPLE。 例如,以下查询使用NEWID函数返回Sales.SalesOrderDetail表的大约百分之一的行:
SELECT * FROM Sales.SalesOrderDetail WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float) / CAST (0x7fffffff AS int)
SalesOrderID列包含在CHECKSUMexpression式中,以便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应该是最后的手段。
从 MSDN上的大型表中随机select行具有简单,明确的解决scheme,可解决大规模性能问题。
SELECT * FROM Table1 WHERE (ABS(CAST( (BINARY_CHECKSUM(*) * RAND()) as int)) % 100) < 10
只需用一个随机数字sorting表,然后使用TOP
获得前5000行。
SELECT TOP 5000 * FROM [Table] ORDER BY newid();
UPDATE
只是试了一下,一个newid()
调用就足够了 – 不需要所有的演员和所有的math。
如果你(不像OP)需要特定数量的logging(这使得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将不接受variables)
这个链接在Orderby(NEWID())和其他有1,700万行的表的方法之间有一个有趣的比较。
通常,当讨论组询问如何select随机行的问题时,build议使用NEWID查询; 它很简单,对于小桌子来说效果很好。
SELECT TOP 10 PERCENT * FROM Table1 ORDER BY NEWID()
但是,NEWID查询在用于大型表格时有一个很大的缺点。 ORDER BY子句会将表中的所有行复制到tempdb数据库中,并在那里对它们进行sorting。 这导致两个问题:
- 分拣操作通常与其相关的成本很高。 sorting可以使用大量的磁盘I / O并且可以运行很长时间。
- 在最坏的情况下,tempdb可能会用尽空间。 在最好的情况下,tempdb可能会占用大量的磁盘空间,如果没有手动收缩命令,永远不会收回。
你需要的是一种随机select不使用tempdb的行,并且在表变大时不会变得太慢。 这是一个新的想法如何做到这一点:
SELECT * FROM Table1 WHERE (ABS(CAST( (BINARY_CHECKSUM(*) * RAND()) as int)) % 100) < 10
这个查询背后的基本思想是我们要为表中的每一行生成一个介于0和99之间的随机数,然后select所有那些随机数小于指定百分比值的行。 在这个例子中,我们想要大约10%的行随机select; 因此,我们select所有的随机数小于10的行。
请阅读MSDN中的完整文章。
在MySQL中,你可以这样做:
SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;
还没有完全看到这个答案的变化。 我有一个额外的约束,我需要,给定一个初始种子,每次select相同的一组行。
对于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(*))
慢得多,所以你可能不想用它来处理大的logging集。
初始种子select:
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一个给定的种子,这似乎工作。
这是最初的种子想法和校验和的组合,它在我看来给出了适当的随机结果,而没有NEWID()的成本:
SELECT TOP [number] FROM table_name ORDER BY RAND(CHECKSUM(*) * RAND())
尝试这个:
SELECT TOP 10 Field1, ..., FieldN FROM Table1 ORDER BY NEWID()
这适用于我:
SELECT * FROM table_name ORDER BY RANDOM() LIMIT [number]
看来newid()不能用在where子句中,所以这个解决scheme需要一个内部查询:
SELECT * FROM ( SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd FROM MyTable ) vw WHERE Rnd % 100 < 10 --10%
我在子查询中使用它,它在子查询中返回相同的行
SELECT ID , ( SELECT TOP 1 ImageURL FROM SubTable ORDER BY NEWID() ) AS ImageURL, GETUTCDATE() , 1 FROM Mytable
然后我解决了包括父表variables在哪里
SELECT ID , ( SELECT TOP 1 ImageURL FROM SubTable Where Mytable.ID>0 ORDER BY NEWID() ) AS ImageURL, GETUTCDATE() , 1 FROM Mytable
注意在哪里condtition