题
我怎么能请求一个随机行(或接近完全随机的,因为可能)在纯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服务器:
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
其他提示
解决方案,如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)
.如果数据集的强烈偏离该假设,你会得到歪曲的结果(其中一些行将出现更多的往往比其他人)。
我不知道如何有效地这样,但我已经用它之前:
SELECT TOP 1 * FROM MyTable ORDER BY newid()
因为Guid漂亮的随机的,排序意味着你得到一个随机行。
ORDER BY NEWID()
需要 7.4 milliseconds
WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table)
需要 0.0065 milliseconds
!
我肯定会去用后一种方法。
你没说是哪一台服务器使用。在旧版本的SQL服务器,可以使用这样的:
select top 1 * from mytable order by newid()
在SQL服务器2005年了,你可以使用 TABLESAMPLE
得到一个随机样品的可重复:
SELECT FirstName, LastName
FROM Contact
TABLESAMPLE (1 ROWS) ;
为SQL服务器
newid()/便通过将工作,但将是非常昂贵的大型结果集,因为它具有以产生id的每一行,然后它们进行排序。
TABLESAMPLE()良好从性能的角度来看,但你会得到结块的结果(所有行的上一页将被退回).
为了更好地执行真正的随机样品,最好的办法是以筛选出行随机。我发现了下列代码样本在SQL服务器书在线文章 限制性的结果套用TABLESAMPLE:
如果你真的想要一个随机样品的 各行,修改您的查询 筛选出行随机的,而不是的 使用TABLESAMPLE.例如, 下查询使用的NEWID 功能返回的大约一个 百分之行 销售。SalesOrderDetail表:
SELECT * FROM Sales.SalesOrderDetail WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float) / CAST (0x7fffffff AS int)
SalesOrderID列包含在 检验和表达这样 NEWID()评估每次行 实现取样于每排的基础上。表达CAST(检验和(NEWID(), SalesOrderID)&0x7fffffff浮/ 投(0x7fffffff为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()/过滤方法。newid()/顺序应当是最后手段,如果你有一大的结果设定的。
如果可能的话,使用存储的发言,以避免效率低下,这两种索引轮()以及创建一个记录的数领域。
PREPARE RandomRecord FROM "SELECT * FROM table LIMIT ?,1"; SET @n=FLOOR(RAND()*(SELECT COUNT(*) FROM table)); EXECUTE RandomRecord USING @n;
最好的方法是把一个随机的价值在新的一列只是为此目的,并利用像这样的东西(pseude代码+SQL):
randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")
这是解决方案所采用的修订编码。当然,有一些偏见对较小值,但是他们发现,这是足以包装的随机的价值约为零时,没有行牵强。
newid()解决方案可能需要一个完整的表格的扫描以便每个行可以分配一个新的guid,这将大大减少高的性能。
rand()解决方案可能无法在所有的工作(即与MSSQL),因为功能进行评估只是一次, 每 行将被分配相同的"随意"数字。
为SQL服务器2005年和2008年,如果我们想要一个随机样品的各个行(从 丛书):
SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)
而不是的 使用RAND(),作为它是不鼓励的, 你可以简单地获得最大ID(=Max):
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
注意,它将检查任何行其Id等于或高于所选择的数值。它也可以追捕行下表中,并获得一个等于或低ID于My_Generated_Random,然后修改的查询是这样的:
SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1
如指出在@BillKarwin的评论@cnu的答案...
当结合有限,我发现它进行更好的(至少与PostgreSQL9.1)加入随机定货,而不是直接以实际行:例如
SELECT * FROM tbl_post AS t
JOIN ...
JOIN ( SELECT id, CAST(-2147483648 * RANDOM() AS integer) AS rand
FROM tbl_post
WHERE create_time >= 1349928000
) r ON r.id = t.id
WHERE create_time >= 1349928000 AND ...
ORDER BY r.rand
LIMIT 100
只要确定'r'产生'兰的价值对每一个可能的关键价值的复杂的查询,这是加入了与它,但仍然的数量限制的行为'r'在可能的情况。
投作为整数是特别有用的PostgreSQL9.2其具有特定的排序优化整数和一个高精度浮动的类型。
大多数的解决方案,这里的目标是避免排序,但他们仍然需要做一个顺序扫描过表。
还有一个方法,以避免的顺序扫描通过切换到指扫描。如果你知道的指标值的随机行,你可以得到的结果几乎instantially.问题是如何猜测指标值。
以下解决方案的工作在PostgreSQL8.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 你可能会错过.解决的办法是 有一个备用计划 :)在这种情况下,纯的旧以通过随机()查询。当结合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;
不是的 联盟 所有 条款。在这种情况下,如果第一部分返回的任何数据的第二种是永远不会执行的!
在晚了,但是得到了在这里通过谷歌,因此,为了子孙后代,我将添加一个替代解决方案。
另一种办法是使用顶级两次,与交替的订单。我不知道如果这是"纯粹的SQL",因为它使用一个变量在顶端,但它的工作原理在SQL服务器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到最(*)的目标表,包含.如果你列索引,你将从中受益。另一个优点是,可以使用这一功能,因为NEWID()是不允许的。
最后,上述运行查询在大约1/10的执行时间NEWID()-类型的查询在同一表格。YYMV.
你也可以尝试使用 new id()
功能。
只是写您的查询和使用订单的 new id()
功能。它很随意。
MySQL以获得随机的记录
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
不相当看到这一变化还没有答案。我有一个额外的制约这里我需要的,鉴于初始种子选择的一组相同的行各一次。
为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 */
如果你需要选择同样的设定的种子,这似乎工作。
在MSSQL(测试11.0.5569)使用
SELECT TOP 100 * FROM employee ORDER BY CRYPT_GEN_RANDOM(10)
明显快于
SELECT TOP 100 * FROM employee ORDER BY NEWID()
在SQL服务器你可以结合TABLESAMPLE与NEWID()得到很好的随机性,仍然有速度。这是特别有用的,如果你真的只想1,或一个少数,排。
SELECT TOP 1 * FROM [table]
TABLESAMPLE (500 ROWS)
ORDER BY NEWID()
SELECT * FROM table ORDER BY RAND() LIMIT 1
我必须同意CD-男人:使用"为了通过RAND()"将工作很好地为小型表,或当你做你的选择只有几次。
我也使用"num_value>=RAND()*..."技术,并且如果我真的想有随机的结果,我有一个特殊的"随意"列表中,我更新一次一天或这样。这个单一的更新运行将需要一些时间(尤其是因为你必须要有一个索引,列),但是它的速度远远超过创造的随机数字为每一行中的每一时间的选择。
要小心,因为TableSample实际上并没有返回的一个随机样品行。它会指引你的查询来看,在随机抽样的8KB页,使你的排。然后,您的查询是执行针对数据包含在这些网页。由于数据如何可以进行分组,这些网页上(插入序,等等),这可能导致数据不是实际上是一个随机样本。
参见: http://www.mssqltips.com/tip.asp?tip=1308
这MSDN页TableSample包括如何产生真正随机抽样的数据。
看来,许多想法列出的仍然使用排序
然而,如果使用一个临时的表中,你能够指定一个随机指数(如许多解决方案的建议),然后抓住第一个大于一个任意数量在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而不是使用dbms_random.值,同时它需要全面扫描,以便行dbms_random.值,这是相当缓慢的大型表格。
用这个代替:
SELECT *
FROM employee sample(1)
WHERE rownum=1
对于火鸟:
Select FIRST 1 column from table ORDER BY RAND()
与SQL服务器2012年+可以使用 偏取查询 要做到这一单随机行
select * from MyTable ORDER BY id OFFSET n ROW FETCH NEXT 1 ROWS ONLY
其中id是一个身份列,n为行你想要的计算,作为一个随机的数量在0和count()-1表(offset0是第一排后,所有)
这适用孔表中的数据,因为只要你有一个索引的工作与为了由的条款。它还非常良好的随机性的-为你工作得出自己的传递。但在其他方法都不存在。另外的表现是相当不错的,在一个较小的数据集,它拥有良好,虽然我已经没有尝试过严重的性能测试数百万排。
为SQL服务器2005年以上的延伸@GreyPanther的答复的情况下时 num_value
没有持续价值观。这工作太对的情况下,当我们没有均匀地分布的数据集和时 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)
)
随机的功能,从sql可能会有帮助。如果你还想要限制到只有一个排,只是添加,在最后。
SELECT column FROM table
ORDER BY RAND()
LIMIT 1