如何在SQL中请求一个随机行?

我如何在纯SQL中请求一个随机行(或尽可能接近真正的随机)?

看到这篇文章: SQL从数据库表中select一个随机的行 。 它通过在MySQL,PostgreSQL,Microsoft SQL Server,IBM DB2和Oracle中执行此操作的方法(以下内容从该链接中复制):

用MySQLselect一个随机的行:

SELECT column FROM table ORDER BY RAND() LIMIT 1 

用PostgreSQLselect一个随机行:

 SELECT column FROM table ORDER BY RANDOM() LIMIT 1 

用Microsoft SQL Serverselect一个随机行:

 SELECT TOP 1 column FROM table ORDER BY NEWID() 

用IBM DB2select一个随机行

 SELECT column, RAND() as IDX FROM table ORDER BY IDX FETCH FIRST 1 ROWS ONLY 

用Oracleselect一个随机logging:

 SELECT column FROM ( SELECT column FROM table ORDER BY dbms_random.value ) WHERE rownum = 1 

像Jeremies这样的解决scheme:

 SELECT * FROM table ORDER BY RAND() LIMIT 1 

工作,但是他们需要对所有表格进行顺序扫描(因为需要计算与每行相关的随机值 – 因此可以确定最小的值),即使是中等大小的表格也是如此。 我的build议是使用某种索引数字列(许多表将这些作为主键),然后写下如下所示的内容:

 SELECT * FROM table WHERE num_value >= RAND() * ( SELECT MAX (num_value ) FROM table ) ORDER BY num_value LIMIT 1 

如果num_value被编入索引,这将在对数时间内工作,不pipe表大小如何。 一个警告:这假设num_value是平均分布在范围0..MAX(num_value) 。 如果你的数据集强烈偏离这个假设,你会得到偏斜的结果(有些行会比其他的更频繁出现)。

我不知道这是多高效,但我之前使用过它:

 SELECT TOP 1 * FROM MyTable ORDER BY newid() 

因为GUID是非常随机的,sorting意味着你得到一个随机的行。

 ORDER BY NEWID() 

需要7.4 milliseconds

 WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table) 

需要0.0065 milliseconds

我一定会用后一种方法去。

你没有说你正在使用哪个服务器。 在旧版本的SQL Server中,可以使用这个:

 select top 1 * from mytable order by newid() 

在SQL Server 2005及更高版本中,您可以使用TABLESAMPLE获取可重复的随机样本:

 SELECT FirstName, LastName FROM Contact TABLESAMPLE (1 ROWS) ; 

对于SQL Server

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应该是最后的手段。

如果可能的话,使用存储的语句来避免RND()上的两个索引效率低下,并创build一个logging号字段。

 PREPARE RandomRecord FROM“SELECT * FROM table LIMIT?,1”;
 SET @ n = FLOOR(RAND()*(SELECT COUNT(*)FROM table));
 EXECUTE RandomRecord USING @n;

对于SQL Server 2005和2008,如果我们想要一个随机样本(来自联机丛书 ):

 SELECT * FROM Sales.SalesOrderDetail WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float) / CAST (0x7fffffff AS int) 

最好的方法就是将一个随机值放在一个新的列中,然后使用类似下面的代码(pseude code + SQL):

 randomNo = random() execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo") 

这是MediaWiki代码使用的解决scheme。 当然,对较小的值有一些偏差,但是他们发现当没有行被提取时将随机值绕到零就足够了。

newid()解决scheme可能需要全表扫描,以便每行可以分配一个新的GUID,这将是性能低得多。

rand()解决scheme可能根本无法工作(即使用MSSQL),因为该函数将被评估一次,并且行将被分配相同的“随机”数字。

正如@ BillKarwin对@​​ cnu答案的评论所指出的那样…

当与LIMIT结合时,我发现它执行得更好(至less在PostgreSQL 9.1中)以随机顺序JOIN,而不是直接sorting实际的行:例如

 SELECT * FROM tbl_post AS t
join...
 JOIN(SELECT id,CAST(-2147483648 * RANDOM()AS integer)AS rand
       从tbl_post
        WHERE create_time> = 1349928000
      )r ON r.id = t.id
 WHERE create_time> = 1349928000 AND ...
 ORDER BY r.rand
限制100

只要确保'r'为与其连接的复杂查询中的每个可能的键值生成一个“rand”值,但是仍然在可能的情况下限制'r'的行数。

CAST as Integer特别有助于PostgreSQL 9.2,它针对整型和单精度浮点types进行了特定的sorting优化。

这里的大多数解决scheme都是为了避免sorting,但是仍然需要在表格上进行顺序扫描。

还有一种方法可以通过切换到索引扫描来避免顺序扫描。 如果您知道随机行的索引值,则可以几乎立即得到结果。 问题是 – 如何猜测索引值。

以下解决scheme适用于PostgreSQL 8.4:

 explain analyze select * from cms_refs where rec_id in (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint from generate_series(1,10)) limit 1; 

我以上的解决scheme,你猜从范围0 .. [ID的最后一个值] 10个不同的随机指标值。

数字10是任意的 – 你可以使用100或1000,因为它(惊人地)对响应时间没有很大的影响。

还有一个问题 – 如果你有稀疏的ID, 你可能会错过 。 解决scheme是有一个备份计划 :)在这种情况下,通过随机()查询纯旧秩序。 当合并id看起来像这样:

 explain analyze select * from cms_refs where rec_id in (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint from generate_series(1,10)) union all (select * from cms_refs order by random() limit 1) limit 1; 

不是工会的 ALL子句。 在这种情况下,如果第一部分返回任何数据,第二部分永远不会执行!

最近,但通过谷歌来到这里,为了后代,我会添加一个替代解决scheme。

另一种方法是使用TOP两次,交替的顺序。 我不知道它是否是“纯SQL”,因为它在TOP中使用了一个variables,但它在SQL Server 2008中起作用。下面是一个例子,我使用了一个字典单词表,如果我需要一个随机单词的话。

 SELECT TOP 1 word FROM ( SELECT TOP(@idx) word FROM dbo.DictionaryAbridged WITH(NOLOCK) ORDER BY word DESC ) AS D ORDER BY word ASC 

当然,@idx是一个随机生成的整数,范围从1到COUNT(*)在目标表上,包含性。 如果您的专栏被编入索引,您也将从中受益。 另一个优点是你可以在一个函数中使用它,因为NEWID()是不允许的。

最后,上述查询运行在同一个表的NEWID()types的查询的执行时间的1/10左右。 YYMV。

你也可以尝试使用new id()函数。

只需编写一个你的查询,并使用new id()函数的顺序。 它非常随机。

对于MySQL来获得随机logging

  SELECT name FROM random AS r1 JOIN (SELECT (RAND() * (SELECT MAX(id) FROM random)) AS id) AS r2 WHERE r1.id >= r2.id ORDER BY r1.id ASC LIMIT 1 

更多详情http://jan.kneschke.de/projects/mysql/order-by-rand/

还没有完全看到这个答案的变化。 我有一个额外的约束,我需要,给定一个初始种子,每次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一个给定的种子,这似乎工作。

在MSSQL(在11.0.5569testing)使用

 SELECT TOP 100 * FROM employee ORDER BY CRYPT_GEN_RANDOM(10) 

明显快于

 SELECT TOP 100 * FROM employee ORDER BY NEWID() 

使用RAND(),因为它不被鼓励 ,你可以简单地得到最大ID(=最大):

 SELECT MAX(ID) FROM TABLE; 

得到1..Max(= My_Generated_Random)

 My_Generated_Random = rand_in_your_programming_lang_function(1..Max); 

然后运行这个SQL:

 SELECT ID FROM TABLE WHERE ID >= My_Generated_Random ORDER BY ID LIMIT 1 

请注意,它将检查任何等于或高于选定值的行。 也可以在表格中查找下一行,获得与My_Generated_Random相同或更低的ID,然后修改查询,如下所示:

 SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1 

在SQL Server中,您可以将TABLESAMPLE和NEWID()组合起来,以获得相当不错的随机性并且仍然有速度。 如果你真的只想要1或者less量的行,这是特别有用的。

 SELECT TOP 1 * FROM [table] TABLESAMPLE (500 ROWS) ORDER BY NEWID() 
  SELECT * FROM table ORDER BY RAND() LIMIT 1 

我必须同意CD-MaN:使用“ORDER BY RAND()”对于小型表格或者只有几次SELECT时才能很好地工作。

我也使用“num_value> = RAND()* …”技术,如果我真的想要随机结果,我有一个特殊的“随机”列,我每天更新一次左右。 这个单独的UPDATE运行需要一些时间(特别是因为你必须在该列上有一个索引),但是比每次运行select时为每行创build随机数要快得多。

要小心,因为TableSample实际上不会返回行的随机样本。 它指导您的查询,查看组成您的行的8KB页面的随机样本。 然后,对这些页面中包含的数据执行查询。 由于数据可能如何在这些页面上分组(插入顺序等),这可能导致数据实际上不是一个随机样本。

请参阅: http : //www.mssqltips.com/tip.asp?tip=1308

TableSample的这个MSDN页面包含了一个如何生成一个实际的随机样本数据的例子。

http://msdn.microsoft.com/en-us/library/ms189108.aspx

看来,列出的许多想法仍然使用sorting

但是,如果使用临时表,则可以指定随机索引(如同许多解决scheme所build议的那样),然后抓取第一个大于0和1之间的任意数字的索引。

例如(对于DB2):

 WITH TEMP AS ( SELECT COMLUMN, RAND() AS IDX FROM TABLE) SELECT COLUMN FROM TABLE WHERE IDX > .5 FETCH FIRST 1 ROW ONLY 

http://akinas.com/pages/en/blog/mysql_random_row/获取简单高效的方法;

 SET @i = (SELECT FLOOR(RAND() * COUNT(*)) FROM table); PREPARE get_stmt FROM 'SELECT * FROM table LIMIT ?, 1'; EXECUTE get_stmt USING @i; 

对于Oracle而言,有更好的解决scheme,而不是使用dbms_random.value,而需要完整扫描才能按dbms_random.value对行进行sorting,而对于大型表而言则相当慢。

用这个代替:

 SELECT * FROM employee sample(1) WHERE rownum=1 

火鸟:

 Select FIRST 1 column from table ORDER BY RAND()