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


当前回答

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

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

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

其他回答

像Jeremies这样的解决方案:

SELECT * FROM table ORDER BY RAND() LIMIT 1

工作,但是它们需要对所有表进行顺序扫描(因为需要计算与每一行相关联的随机值——这样才能确定最小的值),即使对于中等大小的表,这也是相当慢的。我的建议是使用某种索引数字列(许多表都将这些列作为主键),然后编写如下内容:

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

如果num_value被索引,那么无论表大小如何,它都在对数时间内工作。注意:这里假设num_value在0..MAX(num_value)范围内均匀分布。如果您的数据集严重偏离这个假设,您将得到倾斜的结果(一些行会比其他行出现得更频繁)。

对于SQL Server 2005及以上版本,在num_value没有连续值的情况下扩展@GreyPanther的答案。这也适用于数据集分布不均匀以及num_value不是数字而是唯一标识符的情况。

WITH CTE_Table (SelRow, num_value) 
AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ID) AS SelRow, num_value FROM table
) 

SELECT * FROM table Where num_value = ( 
    SELECT TOP 1 num_value FROM CTE_Table  WHERE SelRow >= RAND() * (SELECT MAX(SelRow) FROM CTE_Table)
)
select r.id, r.name from table AS r
INNER JOIN(select CEIL(RAND() * (select MAX(id) from table)) as id) as r1
ON r.id >= r1.id ORDER BY r.id ASC LIMIT 1

这将需要更少的计算时间

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

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

该表的MSDN页面包含了如何生成实际随机数据样本的示例。

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

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

用这个代替:

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