数百万の行を含むpostgresのテーブルがあります。インターネットで確認したところ、次のことがわかりました
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
それは機能しますが、本当に遅いです...そのクエリを作成する別の方法、またはすべてのテーブルを読み取らずにランダムな行を選択する直接的な方法はありますか?ちなみに「myid」は整数ですが、空のフィールドにすることもできます。
数百万の行を含むpostgresのテーブルがあります。インターネットで確認したところ、次のことがわかりました
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
それは機能しますが、本当に遅いです...そのクエリを作成する別の方法、またはすべてのテーブルを読み取らずにランダムな行を選択する直接的な方法はありますか?ちなみに「myid」は整数ですが、空のフィールドにすることもできます。
回答:
あなたは使って実験することがありますOFFSET
のように、
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
N
の行数ですmytable
。最初にを実行SELECT COUNT(*)
して、の値を把握する必要がある場合がありますN
。
更新(Antony Hatchkinsによる)
floor
ここで使用する必要があります:
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
2行のテーブルを考えます。最も近いintへの暗黙的な丸めのため、0行をrandom()*N
生成0 <= x < 2
してSELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
返します。
SELECT COUNT(*)
値を使用するのは理にかなっていますか?つまり、表のすべての値ではなく一部のみを使用していますか?
EXPLAIN SELECT ...
Nの異なる値がクエリのために同じコストを与えるとし、その後、私は推測するNの最大値のために行く方が良いです
PostgreSQL 9.5では、サンプル選択を大幅に高速化する新しいアプローチが導入されました:TABLESAMPLE
構文は
SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage);
SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);
正確なパーセンテージを計算するには、テーブルのCOUNTを知る必要があるため、1つの行のみを選択する場合、これは最適なソリューションではありません。
遅いCOUNTを回避し、1行から数十億行のテーブルに対して高速のTABLESAMPLEを使用するには、次のようにします。
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.000001) LIMIT 1;
-- if you got no result:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.00001) LIMIT 1;
-- if you got no result:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.0001) LIMIT 1;
-- if you got no result:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.001) LIMIT 1;
...
これはそれほどエレガントに見えないかもしれませんが、おそらく他のどの回答よりも高速です。
BERNULLIまたはSYSTEMを使用するかどうかを決定するには、http: //blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/で違いについて読んでください。
SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;
?
SELECT reltuples FROM pg_class WHERE relname = 'my_table'
カウントの推定に使用します。
私はこれをサブクエリで試してみましたが、うまくいきました。オフセット、少なくともPostgresql v8.4.4では正常に動作します。
select * from mytable offset random() * (select count(*) from mytable) limit 1 ;
使用する必要がありますfloor
:
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
random()*N
0 <= x <2を生成し、たとえばSELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
、最も近いintへの暗黙的な丸めのために0行を返します。
order by random()
、3*O(N) < O(NlogN)
実際の数値はインデックスによりわずかに異なります。
WHERE myid NOT IN (1st-myid)
をWHERE myid NOT IN (1st-myid, 2nd-myid)
行うため機能しないことです。うーん...私は2番目と3番目のSELECTでNを1と2減らすことができると思います。
floor()
?それはどんな利点を提供しますか?
いくつかの異なるオプションについては、このリンクをチェックしてください。 http://www.depesz.com/index.php/2007/09/16/my-thoughts-on-getting-random-row/
更新: (A.ハッチキンス)
(非常に)長い記事の要約は次のとおりです。
著者は4つのアプローチをリストします:
1) ORDER BY random() LIMIT 1;
-遅い
2) ORDER BY id where id>=random()*N LIMIT 1
-ギャップがある場合は不均一
3)ランダムな列-時々更新する必要があります
4)カスタムランダム集計 -狡猾な方法、遅い可能性があります:random()はN回生成する必要があります
を使用してメソッド#2を改善することを提案します
5)ORDER BY id where id=random()*N LIMIT 1
結果が空の場合、後続の再クエリ。
ランダムな行をフェッチする最も簡単で最速の方法は、tsm_system_rows
拡張機能を使用することです。
CREATE EXTENSION IF NOT EXISTS tsm_system_rows;
次に、必要な行の正確な数を選択できます。
SELECT myid FROM mytable TABLESAMPLE SYSTEM_ROWS(1);
これは、PostgreSQL 9.5以降で使用できます。
参照:https : //www.postgresql.org/docs/current/static/tsm-system-rows.html
ORDER BY random() LIMIT 1;
十分高速である必要があります。
なしで非常に高速なソリューションを思いつきましたTABLESAMPLE
。よりはるかに速いOFFSET random()*N LIMIT 1
。テーブル数も必要ありません。
たとえば、ランダムではあるが予測可能なデータを使用して式インデックスを作成することmd5(primary key)
です。
以下は、100万行のサンプルデータを使用したテストです。
create table randtest (id serial primary key, data int not null);
insert into randtest (data) select (random()*1000000)::int from generate_series(1,1000000);
create index randtest_md5_id_idx on randtest (md5(id::text));
explain analyze
select * from randtest where md5(id::text)>md5(random()::text)
order by md5(id::text) limit 1;
結果:
Limit (cost=0.42..0.68 rows=1 width=8) (actual time=6.219..6.220 rows=1 loops=1)
-> Index Scan using randtest_md5_id_idx on randtest (cost=0.42..84040.42 rows=333333 width=8) (actual time=6.217..6.217 rows=1 loops=1)
Filter: (md5((id)::text) > md5((random())::text))
Rows Removed by Filter: 1831
Total runtime: 6.245 ms
このクエリは時々(約1 / Number_of_rowsの確率で)0行を返す可能性があるため、確認して再実行する必要があります。また、確率はまったく同じではありません。一部の行は他の行よりも確率が高くなっています。
比較のために:
explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1;
結果は大きく異なりますが、かなり悪い場合があります。
Limit (cost=1442.50..1442.51 rows=1 width=4) (actual time=179.183..179.184 rows=1 loops=1)
-> Seq Scan on randtest (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.016..134.835 rows=915702 loops=1)
Total runtime: 179.211 ms
(3 rows)