从sqlite表中select随机行

我有一个与以下架构的sqlite表:

 CREATE TABLE foo (bar VARCHAR) 

我使用这个表作为string列表的存储。

如何从这个表中select一个随机的行?

看看从SQLite表中select一个随机行

 SELECT * FROM table ORDER BY RANDOM() LIMIT 1; 

下面的解决scheme比anktastic更快(count(*)花费很多,但是如果你可以caching的话,差别不应该那么大),它本身比“random by(order by)当你有很多行时,虽然有一些不方便的地方。

如果你的rowid是相当包装的(即less删除),那么你可以做下面的事情(使用(select max(rowid) from foo)+1而不是max(rowid)+1给出更好的性能, :

 select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)); 

如果你有漏洞,你有时会尝试select一个不存在的rowid,select将返回一个空的结果集。 如果这是不可接受的,你可以提供一个像这样的默认值:

 select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)) or rowid = (select max(rowid) from node) order by rowid limit 1; 

第二种解决scheme并不完美:最后一行(rowid最高的那一行)的概率分布比较高,但是如果你经常在表格中添加东西,它将成为一个移动目标,概率分布应该是好多了。

还有另一种解决scheme,如果你经常从一个有很多洞的表中select随机的东西,那么你可能想创build一个包含原始表的行按随机顺序sorting的表:

 create table random_foo(foo_id); 

然后,定期重新填充表random_foo

 delete from random_foo; insert into random_foo select id from foo; 

而select一个随机的行,你可以使用我的第一个方法(这里没有漏洞)。 当然,这最后一个方法有一些并发问题,但重新构buildrandom_foo是一个维护操作,不可能经常发生。

然而,我最近在邮件列表中发现的另一种方法是在删除时放置一个触发器,将rowid最大的行移动到当前删除的行中,这样就不会留下任何漏洞。

最后,注意rowid和一个整数主键自动增量的行为是不一样的(对于rowid,当插入一个新行时,max(rowid)+1被select,它是最高值,曾经见过+ 1一个主键),所以最后的解决scheme将不会在random_foo中使用自动增量,但其他方法将会。

关于什么:

 SELECT COUNT(*) AS n FROM foo; 

然后在[0,n)中select一个随机数m

 SELECT * FROM foo LIMIT 1 OFFSET m; 

您甚至可以将第一个数字( n )保存在某个地方,并且只在数据库数量发生更改时才进行更新。 这样你就不必每次都做SELECT COUNT了。

 SELECT bar FROM foo ORDER BY Random() LIMIT 1 

这是@ ank的解决scheme的修改:

 SELECT * FROM table LIMIT 1 OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1) 

这个解决scheme也适用于有间隙的索引,因为我们随机化了一个范围[0,count)的偏移量。 MAX用于处理空表的情况。

以下是16k行的表格上的简单testing结果:

 sqlite> .timer on sqlite> select count(*) from payment; 16049 Run Time: real 0.000 user 0.000140 sys 0.000117 sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment); 14746 Run Time: real 0.002 user 0.000899 sys 0.000132 sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment); 12486 Run Time: real 0.001 user 0.000952 sys 0.000103 sqlite> select payment_id from payment order by random() limit 1; 3134 Run Time: real 0.015 user 0.014022 sys 0.000309 sqlite> select payment_id from payment order by random() limit 1; 9407 Run Time: real 0.018 user 0.013757 sys 0.000208 

你需要在你的查询中input“by RANDOM()”

例:

 select * from quest order by RANDOM(); 

我们来看一个完整的例子

  1. 创build一个表格:
 CREATE TABLE quest ( id INTEGER PRIMARY KEY AUTOINCREMENT, quest TEXT NOT NULL, resp_id INTEGER NOT NULL ); 

插入一些值:

 insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24); 

默认select:

 select * from quest; | id | quest | resp_id | 1 1024/4 6 2 256/2 12 3 128/1 24 -- 

一个随机的select:

 select * from quest order by RANDOM(); | id | quest | resp_id | 3 128/1 24 1 1024/4 6 2 256/2 12 -- 

*每次您select,订单将有所不同。

如果你只想返回一行

 select * from quest order by RANDOM() LIMIT 1; | id | quest | resp_id | 2 256/2 12 -- 

*每次您select,返回将是不同的。