从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();
我们来看一个完整的例子
- 创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,返回将是不同的。