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


当前回答

请参阅这篇文章:从数据库表中随机选择一行的SQL。它介绍了在MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2和Oracle中执行此操作的方法(以下内容是从该链接复制的):

用MySQL随机选择一行:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

使用PostgreSQL随机选择一行:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

使用Microsoft SQL Server随机选择一行:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

使用IBM DB2选择一个随机行

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

使用Oracle随机选择一条记录:

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

其他回答

我还没看出来答案有什么不同。我有一个额外的约束条件,给定一个初始种子,每次都要选择相同的行集。

对于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 */

如果给定一个种子,你需要选择相同的集合,这似乎是可行的。

请参阅这篇文章:从数据库表中随机选择一行的SQL。它介绍了在MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2和Oracle中执行此操作的方法(以下内容是从该链接复制的):

用MySQL随机选择一行:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

使用PostgreSQL随机选择一行:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

使用Microsoft SQL Server随机选择一行:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

使用IBM DB2选择一个随机行

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

使用Oracle随机选择一条记录:

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

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

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

最好的方法是在新列中放入一个随机值,并使用如下代码(伪代码+ SQL):

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

这是MediaWiki代码采用的解决方案。当然,对于较小的值会有一些偏差,但他们发现,在没有获取行的情况下,将随机值包装为0就足够了。

Newid()解决方案可能需要全表扫描,以便为每一行分配一个新的guid,这将大大降低性能。

rand()解决方案可能根本不起作用(即与MSSQL),因为函数将只计算一次,并且每一行将被分配相同的“随机”数字。

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

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

以下方案适用于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条款。在这种情况下,如果第一部分返回任何数据,那么第二部分将永远不会执行!