sqlite
次のスキーマを持つテーブルがあります。
CREATE TABLE foo (bar VARCHAR)
このテーブルを文字列のリストのストレージとして使用しています。
このテーブルからランダムな行を選択するにはどうすればよいですか?
Selecting a Random Row from an SQLite Table をご覧ください
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
次のソリューションは、anktastic のソリューションよりもはるかに高速です (count(*) には多くのコストがかかりますが、キャッシュできる場合、違いはそれほど大きくないはずです)。これ自体は、「order by random()」よりもはるかに高速です。行数が多い場合、多少の不便はありますが。
行 ID がかなりパックされている (つまり、削除が少ない) 場合は、次のことを行うことができます (コメントで説明されているように、(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;
この 2 番目の解決策は完全ではありません。確率の分布は最後の行 (ROWID が最も高い行) の方が高くなりますが、テーブルに頻繁に追加すると、移動するターゲットになり、確率の分布は次のようになります。ずっといい。
さらに別の解決策として、多くの穴のあるテーブルからランダムなものを頻繁に選択する場合は、元のテーブルの行をランダムな順序で並べたテーブルを作成することをお勧めします。
create table random_foo(foo_id);
次に、定期的に、テーブル random_foo を再入力します
delete from random_foo;
insert into random_foo select id from foo;
ランダムな行を選択するには、最初の方法を使用できます (ここには穴はありません)。もちろん、この最後の方法には同時実行性の問題がありますが、random_foo の再構築は保守操作であり、あまり頻繁には発生しません。
さらに、最近メーリング リストで見つけたもう 1 つの方法は、最大の行 ID を持つ行を現在削除されている行に移動するように削除にトリガーを置き、穴が残らないようにすることです。
最後に、rowid と整数の主キーの自動インクリメントの動作は同一ではないことに注意してください (rowid では、新しい行が挿入されると max(rowid)+1 が選択されますが、したがって、最後の解決策は random_foo の自動インクリメントでは機能しませんが、他の方法では機能します。
クエリに「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
--
※お選びいただくたびに順番が異なります。
1行だけ返したい場合
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 を実行する必要はありません。
@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
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1