我有一个sqlite
包含以下架构的表:
CREATE TABLE foo (bar VARCHAR)
我正在使用此表作为字符串列表的存储.
如何从此表中选择随机行?
看一下从SQLite表中选择一个随机行
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
以下解决方案比anktastic快得多(计数(*)成本很高,但如果你可以缓存它,那么差异不应该那么大),它本身比"random by random()"要快得多.当你有大量的行时,虽然它们有一些不便之处.
如果您的rowid相当紧凑(即少数删除),那么您可以执行以下操作(使用(select max(rowid) from foo)+1
而不是max(rowid)+1
提供更好的性能,如注释中所述):
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
如果你有洞,你有时会尝试选择一个不存在的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;
第二种解决方案并不完美:最后一行(具有最高rowid的那一行)的概率分布较高,但如果你经常向表中添加东西,它将成为一个移动目标,概率的分布应该是好多了.
还有另一种解决方案,如果您经常从具有大量空洞的表中选择随机内容,那么您可能希望创建一个包含按随机顺序排序的原始表行的表:
create table random_foo(foo_id);
然后,在periodicalliy中,重新填充表random_foo
delete from random_foo; insert into random_foo select id from foo;
要选择一个随机行,您可以使用我的第一个方法(这里没有漏洞).当然,最后一种方法存在一些并发性问题,但是重新构建random_foo是一种不太经常发生的维护操作.
然而,我最近在邮件列表中找到的另一种方法是在删除时设置触发器,将具有最大rowid的行移动到当前删除的行中,这样就不会留下任何漏洞.
最后,请注意rowid和整数主键自动增量的行为不相同(使用rowid,当插入新行时,选择max(rowid)+1,其中它是高亮值,有史以来+ 1为一个主键),所以最后的解决方案不适用于random_foo中的自动增量,但其他方法将.
关于什么:
SELECT COUNT(*) AS n FROM foo;
然后在[0,n)中选择一个随机数m
SELECT * FROM foo LIMIT 1 OFFSET m;
您甚至可以在某处保存第一个数字(n),并仅在数据库计数更改时更新它.这样你就不必每次都做SELECT COUNT.
您需要在查询中输入"按RANDOM()排序".
例:
select * from quest order by RANDOM();
让我们看一个完整的例子
创建一个表:
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 * from quest; | id | quest | resp_id | 1 1024/4 6 2 256/2 12 3 128/1 24 --
选择随机:
select * from quest order by RANDOM(); | id | quest | resp_id | 3 128/1 24 1 1024/4 6 2 256/2 12 --*每次选择时,订单都会有所不同.
如果您只想返回一行
select * from quest order by RANDOM() LIMIT 1; | id | quest | resp_id | 2 256/2 12 --*每次选择时,退货都会有所不同.
SELECT bar FROM foo ORDER BY Random() LIMIT 1
这是@ank解决方案的修改:
SELECT * FROM table LIMIT 1 OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)
该解决方案也适用于带有间隙的索引,因为我们将偏移量随机化在[0,count)范围内。MAX
用于处理空表的情况。
以下是对具有16k行的表的简单测试结果:
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