我有一个sqlite
具有以下架构的表:
CREATE TABLE foo (bar VARCHAR)
我正在使用此表作为字符串列表的存储。
如何从此表中选择随机行?
我有一个sqlite
具有以下架构的表:
CREATE TABLE foo (bar VARCHAR)
我正在使用此表作为字符串列表的存储。
如何从此表中选择随机行?
Answers:
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;
我总是得到同一行。
以下解决方案比anktastic的解决方案要快得多(count(*)花费很多,但如果可以对其进行缓存,则差异不应该那么大),其本身比“ order by random()”要快得多。当您有很多行时,尽管它们有一些不便。
如果您的行标识符比较紧凑(即删除了很少的内容),则可以执行以下操作(使用(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 * 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的重建是一项维护操作,不太可能经常发生。
但是,我最近在邮件列表上发现的另一种方法是,在delete上放置一个触发器,以将具有最大rowid的行移动到当前已删除的行中,从而不留任何空洞。
最后,请注意,rowid和整数主键自动递增的行为是不相同的(对于rowid,当插入新行时,选择了max(rowid)+1,对于主键),因此最后一个解决方案无法在random_foo中使用自动递增功能,但其他方法则可以。
SELECT max(rowid) + 1
这将是一个缓慢的查询-它需要全表扫描。sqlite仅优化查询SELECT max(rowid)
。因此,可以通过以下方法改善此答案: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
有关更多信息,请参见:sqlite.1065341.n5.nabble.com/…–
您需要在查询中放置“ order by 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 COUNT(*) AS n FROM foo;
然后在[0,n)中选择一个随机数m
SELECT * FROM foo LIMIT 1 OFFSET m;
您甚至可以将第一个数字(n)保存在某个地方,并且仅在数据库计数更改时才对其进行更新。这样,您不必每次都执行SELECT COUNT。
OFFSET
偏移量所需的时间似乎取决于偏移量的大小-第2行很快,第2百万行花费了一段时间,即使其中的所有数据都是固定大小并且应该能够直接寻求它。至少,这就是SQLite 3.7.13中的样子。
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
我为大型sqlite3数据库提出了以下解决方案:
SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1;
abs(X)函数返回数字参数X的绝对值。
random()函数返回-9223372036854775808和+9223372036854775807之间的伪随机整数。
运算符%将其左操作数的整数值乘以其右操作数。
最后,您添加+1以防止rowid等于0。