从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。 这导致两个问题:

  1. 分拣操作通常与其相关的成本很高。 sorting可以使用大量的磁盘I / O并且可以运行很长时间。
  2. 在最坏的情况下,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