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


当前回答

Oracle有更好的解决方案,而不是使用dbms_random。值,而它需要完全扫描dbms_random来排序行。值,对于大表来说非常慢。

用这个代替:

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

其他回答

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

select top 1 * from mytable order by newid()

在SQL Server 2005及以上版本中,你可以使用TABLESAMPLE来获取一个可重复的随机样本:

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

这里的大多数解决方案都旨在避免排序,但它们仍然需要对表进行顺序扫描。

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

以下方案适用于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;

我上面的解决方案你猜10个不同的随机指标值从范围0 ..[id的最后一个值]。

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

还有一个问题-如果你有稀疏的id,你可能会错过。解决方案是有一个备份计划:)在这种情况下,一个纯旧order by random()查询。当合并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;

不是union ALL条款。在这种情况下,如果第一部分返回任何数据,那么第二部分将永远不会执行!

在SQL Server中,您可以将TABLESAMPLE与NEWID()结合使用,以获得相当好的随机性,并且仍然具有速度。如果您真的只想要1行或少量的行,这尤其有用。

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

我不得不同意CD-MaN:使用“ORDER BY RAND()”将很好地用于小表或当你只做几次SELECT时。

我还使用“num_value >= RAND() *…”技术,如果我真的想获得随机结果,我在表中有一个特殊的“随机”列,我大约每天更新一次。单个UPDATE运行将花费一些时间(特别是因为必须在该列上建立索引),但它比每次运行select时为每一行创建随机数快得多。

对于SQL Server 2005和2008,如果我们想要一个随机的个别行样本(来自Books Online):

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