从sqlite表中选择随机行

时间:2010-02-17 10:04:53

标签: sqlite random row

我有一个sqlite表,其中包含以下架构:

CREATE TABLE foo (bar VARCHAR)

我正在使用此表作为字符串列表的存储。

如何从此表中选择随机行?

7 个答案:

答案 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)和

中选择一个随机数 m
SELECT * FROM foo LIMIT 1 OFFSET m;

您甚至可以在某处保存第一个数字( n ),并仅在数据库计数更改时更新它。这样你就不必每次都做SELECT COUNT。

答案 3 :(得分:13)

您需要在查询中输入"按RANDOM()" 排序。

示例:

select * from quest order by RANDOM();

让我们看一个完整的例子

  1. 创建一个表格:
  2. 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.