私が持っているsqlite
次のスキーマを持つテーブルを:
CREATE TABLE foo (bar VARCHAR)
このテーブルを文字列のリストのストレージとして使用しています。
このテーブルからランダムな行を選択するにはどうすればよいですか?
私が持っているsqlite
次のスキーマを持つテーブルを:
CREATE TABLE foo (bar VARCHAR)
このテーブルを文字列のリストのストレージとして使用しています。
このテーブルからランダムな行を選択するにはどうすればよいですか?
回答:
SQLiteテーブルからのランダムな行の選択を見てください
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;
と常に同じ行が表示されます。
次の解決策は、antkasticの場合よりもはるかに高速です(count(*)は多くのコストがかかりますが、キャッシュできる場合、その差はそれほど大きくないはずです)。これは、「order by random()」よりはるかに高速ですいくつかの不便がありますが、多数の行がある場合。
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 * 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つの方法は、削除のトリガーを設定して、最大の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
--
*選択するたびに、順序は異なります。
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を実行する必要がなくなります。
OFFSET
、オフセットのサイズに応じてを見つけるために必要な時間が増えるようです-行2は高速で、行200万は時間がかかります。直接それを探すことができるはずです。少なくとも、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になるのを防ぎます。