我有一个sqlite
表,其中包含以下架构:
CREATE TABLE foo (bar VARCHAR)
我正在使用此表作为字符串列表的存储。
如何从此表中选择随机行?
答案 0 :(得分:182)
查看Selecting a Random Row from an SQLite Table
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
答案 1 :(得分:28)
以下解决方案比anktastic快得多(计数(*)成本很高,但如果你可以缓存它,那么差异不应该那么大),它本身比“随机顺序”快得多()“当你有大量的行时,虽然它们有一些不便之处。
如果您的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);
然后,期刊,重新填写表random_foo
delete from random_foo;
insert into random_foo select id from foo;
要选择随机行,您可以使用我的第一种方法(这里没有漏洞)。当然,最后一种方法存在一些并发性问题,但是重新构建random_foo是一种不太可能经常发生的维护操作。
然而,另一种方式,我最近在mailing list找到的方法是在删除时设置触发器,将具有最大rowid的行移动到当前删除的行中,这样就不会留下任何空洞。 / p>
最后,请注意rowid和整数主键自动增量的行为不相同(使用rowid,当插入新行时,选择max(rowid)+1,这是有史以来最高值的主键为+1),因此最后一个解决方案不适用于random_foo中的自动增量,但其他方法也适用。
答案 2 :(得分:16)
怎么样:
SELECT COUNT(*) AS n FROM foo;
然后在[0,n)和
中选择一个随机数 mSELECT * FROM foo LIMIT 1 OFFSET m;
您甚至可以在某处保存第一个数字( n ),并仅在数据库计数更改时更新它。这样你就不必每次都做SELECT COUNT。
答案 3 :(得分:13)
您需要在查询中输入"按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
--
*每次选择时,回报都会有所不同。
答案 4 :(得分:9)
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
答案 5 :(得分:5)
以下是@ 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
答案 6 :(得分:4)
我为大型sqlite3数据库提出了以下解决方案:
SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1;
abs(X)函数返回数字参数的绝对值 X.
random()函数返回一个伪随机整数 -9223372036854775808和+9223372036854775807。
运算符%输出其左操作数的整数值以及其右操作数的模数。
最后,添加+1以防止rowid等于0.