web-dev-qa-db-ja.com

PostgreSQLテーブルから真にランダムな行をすばやく取得する

私はいつもやっていました:

SELECT column FROM table ORDER BY random() LIMIT 1;

大きなテーブルの場合、これは耐えられないほど、非常に遅いため、実際には役に立たないほどです。そのため、より効率的な方法を探し始めました。おすすめの人:

SELECT column FROM table TABLESAMPLE BERNOULLI(1) LIMIT 1;

高速ですが、価値のないランダム性も提供します。いつも同じレコードを選ぶように見えるので、これも価値がありません。

私も試しました:

SELECT column FROM table TABLESAMPLE BERNOULLI(100) LIMIT 1;

さらに悪いランダム性を与えます。毎回同じ数のレコードを選択します。これはまったく価値がありません。実際のランダム性が必要です。

ランダムなレコードを選ぶだけではどうしてそんなに難しいのですか?なぜすべてのレコードを取得してからソートする必要があるのですか(最初の場合)?また、「TABLESAMPLE」バージョンが常に同じ愚かなレコードを取得するのはなぜですか?なぜ彼らはまったくランダムではないのですか?同じ「BERNOULLI」のものを何度も何度も同じレコードを選択するときに、誰がこの「ベルヌーイ」のものを使いたいと思うでしょうか?私はまだ、これらすべての年の後に、ランダムなレコードを取得することについて尋ねているとはまだ信じられません...これは、可能な最も基本的なクエリの1つです。

まともなサイズのテーブルで数秒かかるほど遅くないPGのテーブルからランダムなレコードを取得するために使用する実際のコマンドは何ですか?

1
Maurice

興味深い質問-これには多くの可能性/順列があります(この回答は大幅に改訂されました)。

基本的に、この問題は2つの主な流れに分けることができます。

  • 単一のランダムなレコード

  • 複数のランダムなレコード(質問にはありません-下部の参照と説明を参照してください)

researched これがあるため、単一レコードの問題に対する最速の解決策は、Evan Carrollが提供するPostgreSQLの _tsm_system_rows_ 拡張機能を使用することだと思います 答え

バイナリディストリビューションを使用している場合はわかりませんが、 contrib モジュール(_tsm_system_rows_は1つ)がデフォルトで使用できると思います-少なくとも、私がWindowsテストに使用した EnterpriseDB Windows バージョン用でした(以下を参照)。私の主なテストは、Linux(_make world_および_make install-world_)のソースからコンパイルされた12.1で行われました。

単一レコードのユースケースに最適であると私が思う理由は、この拡張機能に関して言及されている唯一の問題は、

組み込みのSYSTEMサンプリング方法と同様に、SYSTEM_ROWSはブロックレベルのサンプリングを実行するため、サンプルは完全にランダムではありませんが、特に少数の行のみが要求される場合は、クラスタリングの影響を受ける可能性があります。

ただし、関心があるのは1行だけなので、ブロックレベルのクラスタリング効果は問題になりません。 この記事 2ndQuadrantは、oneレコードのサンプルでこれが問題にならない理由を示しています!これは、小さなサブセットの主要な問題です(投稿の最後を参照)-[〜#〜]または[〜#〜] 1つの大きなテーブルからランダムなレコードの大きなサンプルを生成したい場合(ここでも、以下の_tsm_system_rows_および_tsm_system_time_の説明を参照してください)。

次に、次のようなテーブルを作成してデータを入力しました。

_CREATE TABLE Rand AS SELECT generate_series(1, 100000000) AS seq, MD5(random()::text);
_

これで、1億件(1億件)のレコードを持つテーブルができました。次に、_PRIMARY KEY_を追加しました。

_ALTER TABLE Rand ADD PRIMARY KEY (seq);
_

それで、今度はSELECTランダムレコードに:

_SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);
_

少し変更したコマンドを使用して、ランダムさを「確認」できるようにしたことに注意してください。また、_\timing_コマンドを設定して、経験的な測定値を取得できるようにしました。

LENGTH() 関数を使用して、返される_PRIMARY KEY_整数のサイズをすぐに認識できるようにしました。返されるレコードのサンプルは次のとおりです。

_test=# SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);
 length | ?column?  |               md5                
--------+-----------+----------------------------------
      6 | 970749.61 | bf18719016ff4f5d16ed54c5f4679e20
(1 row)

Time: 30.606 ms
test=# SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);
 length | ?column?  |               md5                
--------+-----------+----------------------------------
      6 | 512101.21 | d27fbeea30b79d3e4eacdfea7a62b8ac
(1 row)

Time: 0.556 ms
test=# SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);
 length | ?column?  |               md5                
--------+-----------+----------------------------------
      6 | 666476.41 | c7c0c34d59229bdc42d91d0d4d9d1403
(1 row)

Time: 0.650 ms
test=# SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);
 length | ?column? |               md5                
--------+----------+----------------------------------
      5 | 49152.01 | 0a2ff4da00a2b81697e7e465bd67d85c
(1 row)

Time: 0.593 ms
test=# SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);
 length | ?column? |               md5                
--------+----------+----------------------------------
      5 | 18061.21 | ee46adc96a6f8264a5c6614f8463667d
(1 row)

Time: 0.616 ms
test=# SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);
 length | ?column?  |               md5                
--------+-----------+----------------------------------
      6 | 691962.01 | 4bac0d051490c47716f860f8afb8b24a
(1 row)

Time: 0.743 ms
_

ご覧のように、LENGTH()関数はほとんどの場合6を返します。ほとんどのレコードは10,000,000から100,000,000の間であるため、これは予想されますが、値5を示すカップルがあります( 3と4の値も見られます-データは表示されていません)。

今、タイミングに注意してください。最初は30ミリ秒(ms)ですが、残りはサブミリ秒(約0.6-0.7ms)です。 ランダムサンプルのほとんどがこのサブミリ秒の範囲で返されますが、25〜30 msで結果が返されます(平均して3または4分の1)。

時々、このマルチミリ秒の結果は2回または3回続けて発生する可能性がありますが、前述のように、結果の大部分(約66-75% )はサブミリ秒です。 私が見た私のソリューションの応答時間のなしは75msを超えていません。

調査中に、_tsm_system_time_に似た _tsm_system_rows_ 拡張機能も発見しました。今、私はこの拡張機能を次のようにベンチマークしました:

_SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_TIME(0.001) LIMIT 1;
_

タイムクォンタムはマイクロ秒であるミリ秒の1/1000であることに注意してください。これより小さい値を入力すると、レコードは返されません。ただし、興味深いことに、この小さな量子alwaysでも120行を返します。

120が給与水準を少し上回っている理由- PostgreSQLページサイズ は8192(デフォルト)です

_test=# SELECT current_setting('block_size');
 current_setting 
-----------------
 8192
(1 row)
_

_file system block size_ は4096です

_[pol@UNKNOWN inst]$blockdev --getbsz /dev/mapper/Fedora_localhost--live-home 
4096
_

レコードは(1 INTEGER(4バイト)+ 1 UUID(16バイト))(= 20バイト)+ seqフィールドのインデックス(サイズ?) 。 4096/120 = 34.1333 ...-このテーブルの各インデックスエントリに14バイトかかるとは思いません-したがって、120がどこから来たかはわかりません。

LIMIT句が常にページまたはブロックの最初のタプルを返すかどうか、私はよくわかりません-それにより、方程式にランダムでない要素を導入します。

_tsm_system_time_クエリのパフォーマンスは、_tsm_system_rows_拡張機能のパフォーマンスと同じです(データは表示されていません)。これらの拡張機能が最初のレコードを選択する方法によって導入される非ランダム性の要素があるかどうかわからないという同じ警告は、_tsm_system_rows_クエリにも適用されます。以下のこれら2つの方法の(いわゆる)ランダム性についての説明とベンチテストを参照してください。

参考までに、パフォーマンスに関しては、Dell Studio 1557を1TB HDD(回転するRust)と8GBのDDR3で使用していますRAM Fedora 31を実行)。これは10年前のマシンです。

私はまた、マシンで同じことを行いました(Packard Bell、EasyNote TM-10年前、8 GB DDR3 RAM))SSDを使用しています(SSDはSSDの上部ではありません)そして、応答時間は通常(奇妙なことに)少し高い(〜1.3 ms)が、スパイクは少なく、これらの値は低い(〜5〜7 ms)。

2019 Serverを使用すると、バックグラウンドで多くのものが実行される可能性がありますが、SSDが適切​​な最新のラップトップを使用している場合は、当然のことながら、ミリ秒未満の応答時間を期待できないわけではありません。

すべてのテストはPostgreSQL 12.1を使用して実行されました。

両方の方法の真の「ランダム性」を確認するために、次の表を作成しました。

_CREATE TABLE Rand_samp 
(
  seq INT, 
  md5 TEXT
);
_

次に実行しました(各3回):

_DO
$$
DECLARE 
  i RECORD;
BEGIN
  FOR i IN 1..10000 LOOP
    INSERT INTO Rand_samp (seq, md5)
    SELECT seq, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);  
  END LOOP;
END;
$$
;
_

また、(上記の関数の内部ループで)を使用します

_SELECT seq, md5 FROM Rand TABLESAMPLE SYSTEM_TIME(0.001) LIMIT 1;
_

次に、実行するたびに、_Rand_samp_テーブルをクエリしました。

_SELECT 
  seq, COUNT(seq) 
FROM Rand_samp 
GROUP BY seq 
HAVING COUNT(seq) > 1;
_

そして、次のカウントを得ました:

_TABLESAMPLE SYSTEM_ROWS_の場合、258、63、44の重複、allを2のカウントで取得しました。_TABLESAMPLE SYSTEM_TIME_の場合、46、54、62を取得しました。再びallでカウント2。

さて、私の統計は少し錆びていますが、100Mレコードのテーブルのランダムなサンプルから、10,000のサンプルから(Randテーブルのレコード数の1万分の1)、私はdいくつかの重複が予想されます-たまに時々ですが、何もない私が得た数のように。さらに、真のランダム性があった場合、私は(少数の)3と4も期待します。

私は_TABLESAMPLE SYSTEM_ROWS_を使用して100,000回の実行で2つのテストを実行し、最初の実行で5540の重複(3重複で〜200と4の重複で6)を取得し、2回目の実行で5465重複(〜200で3と6で4)を取得しました。 most興味深いクエリはこれでした:

_SELECT COUNT(s.seq)
FROM Rand_samp s
WHERE s.seq IN (SELECT sb.seq FROM Rand_samp_bis sb);
_

ここで、両方の100,000回のデュープを互いに比較します-答えはなんと11,250(> 10%)は同じです-サンプルの1000分の1(1/1000)はです[〜#〜]方法[〜#〜]チャンスが大幅に減ります!

結果100,000は_SYSTEM_TIME_に対して実行されます-5467の複製、215が3、9が4で最初のグループ、5472、210(3)、12(4)が2番目のグループ。一致するレコードの数は11,328(これも10%以上)です。

明らかに(LOT of)非ランダムな動作が行われています。 OPに任せて、速度とランダムのトレードオフが価値があるかどうかを判断します。

他の回答のベンチマーク。

私は他の提案されたソリューションをベンチマークすることを決めました-上から私の1億レコードテーブルを使用します。すべてのテストを5回実行しました-一連のテストの最初に異常値を無視して、キャッシュ/影響を排除しました。すべての外れ値は、以下に報告されている値よりも高かった。

HDDを搭載したマシンを使用しています。後でSSDマシンでテストします。報告された_.mmm_はミリ秒を意味します-私の答えでは重要ではありません。

DanielVérité の答え:

_SELECT * FROM
  (SELECT seq FROM Rand TABLESAMPLE BERNOULLI(1)) AS s
 ORDER BY RANDOM() LIMIT 1;
_

5回実行した-すべての時間は1分を超えました-通常01:00.mmm(1時01:05.mmm)。

典型的な実行:

_test=# SELECT * FROM
  (SELECT seq FROM Rand TABLESAMPLE BERNOULLI(1)) AS s
 ORDER BY RANDOM() LIMIT 1;
   seq   
---------
 9529212
(1 row)

Time: 60789.988 ms (01:00.790)
_

Swav の答え:

_SELECT md5 FROM Rand OFFSET (
    SELECT floor(random() * (SELECT count(seq) from Rand))::int
) limit 1;
_

5回実行-すべての時間は1分以上-01:03から01:29まで

典型的な実行:

_test=# SELECT md5 FROM Rand OFFSET (
    SELECT floor(random() * (SELECT count(seq) from Rand))::int
) limit 1;
               md5                
----------------------------------
 8004dfdfbaa9ac94243c33e9753e1f77
(1 row)

Time: 68558.096 ms (01:08.558)
_

Colin 't Hart の答え:

_select * from Rand where seq >= (
  select random()*(max(seq)-min(seq)) + min(seq) from Rand
)
order by seq
limit 1;
_

5回実行-時間は00:06.mmmと00:14.mmmの間で変化しました(残りのベスト!)

典型的な実行:

_test=# select * from Rand where seq >= (
  select random()*(max(seq)-min(seq)) + min(seq) from Rand
)
order by seq
limit 1;
   seq    |               md5                
----------+----------------------------------
 29277339 | 2b27c594f65659c832f8a609c8cf8e78
(1 row)

Time: 6944.771 ms (00:06.945)
_

Colin 't Hart の2番目の回答(私が採用):

_WITH min_max AS MATERIALIZED -- or NOT, doesn't appear to make a difference
(
  SELECT MIN(seq) AS min_s, MAX(seq) AS max_s, (MAX(seq) - MIN(seq)) - MIN(seq) AS diff_s
  FROM Rand
),
other  AS MATERIALIZED
(
  SELECT FLOOR(RANDOM() * (SELECT diff_s FROM min_max))::INT AS seq_val
)
SELECT seq, md5 
FROM Rand
WHERE seq = (SELECT seq_val FROM other);
_

応答時間は約30〜45ミリ秒で、これらの時間のいずれかの側に奇数の外れ値があり、時々1.xxxミリ秒まで低下することさえあります。私が本当に言えることは、それが_SYSTEM_TIME_および_SYSTEM_ROWS_メソッドのどちらよりも一貫しているように見えることです。

ただし、この方法にはmajorの問題があります。ランダム性のために選択している基礎となるフィールドがスパースである場合、このメソッドは常に値を返しません-これはmayまたはmay OPに受け入れられない? (クエリの終了)のようなことができます:

_SELECT seq, md5 
FROM Rand
WHERE seq >= (SELECT seq_val FROM other)
LIMIT 1;
_

(_>=_および_LIMIT 1_に注意してください)。これは非常に効率的です(1.xxxミリ秒)が、_seq =..._の定式化だけではないように見えますが、キャッシュがウォームアップされているように見えると、定期的に約1.5ミリ秒の応答時間が得られます。

このソリューションの別の利点は、コンテキストに応じて特別な拡張機能を必要としない(コンサルタントに「特別な」ツールのインストール、DBAルールの許可がない...)利用できない場合があります。

上記のソリューションの本当にWEIRDの1つは、_::INT_ CASTが削除された場合、クエリに1分ほどかかることです。 FLOOR 関数はINTEGERを返す必要がありますが、これは発生します。 EXPLAIN (ANALYZE BUFFERS)を実行したところ、これが問題であることがわかりました。

:: INT

_   CTE other
     ->  Result  (cost=0.02..0.04 rows=1 width=4) (actual time=38.906..38.907 rows=1 loops=1)
           Buffers: shared hit=1 read=9
           InitPlan 4 (returns $3)
             ->  CTE Scan on min_max  (cost=0.00..0.02 rows=1 width=4) (actual time=38.900..38.902 rows=1 loops=1)
                   Buffers: shared hit=1 read=9
   InitPlan 6 (returns $5)
     ->  CTE Scan on other  (cost=0.00..0.02 rows=1 width=4) (actual time=38.909..38.910 rows=1 loops=1)
           Buffers: shared hit=1 read=9
 Planning Time: 0.329 ms
 Execution Time: 68.449 ms
(31 rows)

Time: 99.708 ms
test=#
_

:: INTなし

_   CTE other
     ->  Result  (cost=0.02..0.04 rows=1 width=8) (actual time=0.082..0.082 rows=1 loops=1)
           Buffers: shared hit=10
           InitPlan 4 (returns $3)
             ->  CTE Scan on min_max  (cost=0.00..0.02 rows=1 width=4) (actual time=0.076..0.077 rows=1 loops=1)
                   Buffers: shared hit=10
   InitPlan 6 (returns $5)
     ->  CTE Scan on other  (cost=0.00..0.02 rows=1 width=8) (actual time=0.085..0.085 rows=1 loops=1)
           Buffers: shared hit=10
   ->  Parallel Seq Scan on Rand  (cost=0.00..1458334.00 rows=208333 width=37) (actual time=52644.672..60025.906 rows=0 loops=3)
         Filter: ((seq)::double precision = $5)
         Rows Removed by Filter: 33333333
         Buffers: shared hit=14469 read=818865
 Planning Time: 0.378 ms
 Execution Time: 60259.401 ms
(37 rows)

Time: 60289.827 ms (01:00.290)
test=#
_

(_::INT_なし)

_   ->  Parallel Seq Scan on Rand  (cost=0.00..1458334.00 rows=208333 width=37) (actual time=52644.672..60025.906 rows=0 loops=3)
         Filter: ((seq)::double precision = $5)
_

並列シーケンススキャン(高コスト)、(seq):: doubleでフィルター

WHY double ??).

そして

_Buffers: shared hit=14469 read=818865
_

(with _::INT_)と比較

_Buffers: shared hit=1 read=9
_

最後に、私自身の答え(同じマシン、時間、キャッシュ):

(これは、上記で実行されたベンチマークに照らして今では冗長です)。

自分のベンチマークを再度15回実行しました。通常、ミリ秒未満の時間で、時々(約3/4に1回)の実行には約25ミリ秒。

典型的な実行:

_test=# SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(1);
 length | ?column?  |               md5                
--------+-----------+----------------------------------
      6 | 401578.81 | 30ff7ecfedea088dab75932f0b1ea872
(1 row)

Time: 0.708 ms
_

したがって、私のソリューションの最悪の時間は、パックの残りの回答の中で最も速い(Colin 't Hart)よりも200倍速いようです。

私の分析は、noの完全な解があるということですが、最良の解決策はColin 't Hartの解の適応です。

最後に、[〜#〜] graphic [〜#〜]複数のレコードに対してこのソリューションを使用することに関連する問題のデモンストレーションを以下に示します-25レコードのサンプルを取ります(数回実行-典型的な実行が示されています)。

tsm_system_rows メソッドは、25の順次レコードを生成します。これは、ランダムサンプルが連続したレコードの数であるという事実が問題ではない特定の目的に適している場合がありますが、覚えておく価値があります。

_test=# SELECT LENGTH((seq/100)::TEXT), seq/100::FLOAT, md5 FROM Rand TABLESAMPLE SYSTEM_ROWS(25);
 length | ?column?  |               md5                
--------+-----------+----------------------------------
      6 | 763140.01 | 7e84b36ab30d3d2038ebd832c241b54d
      6 | 763140.02 | a976e258f6047df18e8ed0559ff48c36
--
--    SEQUENTIAL values of seq!
--
      6 | 763140.23 | ad4f1b4362187d6a300aaa9aaef30170
      6 | 763140.24 | 0c7fcc4f07d27fbcece68b2503f28d88
      6 | 763140.25 | 64d4507b18b5481a724d8a5bb6ac59c8
(25 rows)
_

時間:29.348ミリ秒

同様の状況は、_SYSTEM_TIME_メソッドの場合にも当てはまります。上記のように、最小時間1μsでも120レコードが得られます。 _SYSTEM_ROWS_と同様に、これらは_PRIMARY KEY_の順次値を提供します。

_test=# SELECT seq, md5 FROM Rand TABLESAMPLE SYSTEM_TIME(0.001);
_

戻り値:

_   seq    |               md5                
----------+----------------------------------
 42392881 | e92f15cba600f0c7aa16db98c0183828
 42392882 | 93db51ea870e15202144d11810c8f40c
 42392883 | 7357bf0cf1fa23ab726e642832bb87b0
 42392884 | 1f5ce45fb17c8ba19b391f9b9c835242
 42392885 | f9922b502d4fd9ee84a904ac44d4e560
 ...
 ...  115 sequential values snipped for brevity!
_

私たちの姉妹サイトであるStackOverflowは、まさにこの問題 here を扱いました。 (まだ)Erwin Brandstetter here およびEvan Carroll here が適切な回答を提供しています。そのスレッド全体は詳細に読む価値があります-random(単調に増加/減少、 _Pseudorandom number generators_ ...)とsamplingの定義が異なるため(交換あり/なし...)。

7
Vérace

あなたの間違いは、常にサンプルの最初の行を取ることです。

代わりにランダムな行を取得します。

SELECT * FROM
  (SELECT column FROM table TABLESAMPLE BERNOULLI(1)) AS s
 ORDER BY RANDOM() LIMIT 1;

サンプルの内容はランダムですが、サンプルの順序はランダムではありません。サンプリングはテーブルスキャンを行うため、テーブルの順序で行が生成される傾向があります。これは、新しく作成され、完全に順序付けられたテーブルを見ると明らかです。

create table a as select * from generate_series(1,1000000) as i;

select * from a tablesample bernoulli(1) limit 10;
  i   
------
  248
  394
  463
  557
  686
  918
  933
 1104
 1124
 1336
(10 rows)

サンプルにLIMITを直接適用すると、ディスク上のテーブルの順序でテーブルの最初から常に小さい値が生成される傾向があります。これはLIMIT 1ではさらに悪くなります。

これを正しい方法と比較してください:

select * from (select * from a tablesample bernoulli(1) ) s order by random() limit 10;
   i    
--------
 622931
 864123
 817263
 729949
 748422
 127263
 322338
 900781
  49371
 616774
4
Daniel Vérité

(ほとんどの場合)順次IDがあり、その列に主キーがある場合(ある場合)に役立つ別のアプローチ:

最初に、IDの最小値と最大値を見つけます。これはインデックスを使用します。

次に、これらの2つの値の間に乱数を生成します。

最後に、ランダム値以上のIDを持つ最初の行を選択します。これもインデックスを使用します。

クエリは次のようになります。

_select * from mytable where id >= (
  select random()*(max(id)-min(id))+min(id) from mytable
)
order by id
limit 1;
_

Postgres 12でテスト-必要に応じて、explain analyzeを挿入して実行プランを表示します。

https://dbfiddle.uk/?rdbms=postgres_12&fiddle=ede64b836e76259819c10cb6aecc7c84

[〜#〜]更新[〜#〜]

@Véraceのベンチマークに対応して、いくつかのテストを行いました。 DBのバージョンFiddleは高速に実行されているように見えましたが、Postgres 12.1がローカルで実行されている場合にも問題がありました。

これに対応して、2つの変更を加えました。

  1. 副選択のround()で_>=_演算子を_=_に置き換えました。

  2. クエリを2つに分割しました。おそらくルールに反していますか?

_colin=> select round(random()*(max(seq)-min(seq)) + min(seq)) from Rand;
  round   
----------
  23656425
(1 row)

Time: 0,394 ms
colin=> select * from Rand where seq = 23656425;
   seq    |               md5                
----------+----------------------------------
 23656425 | ba9777a8a6e14e3ce21d55c7f6089fe5
(1 row)

Time: 99,307 ms
colin=>
_

今、私は約100msの時間を取得します。 2つのクエリを組み合わせることができるかどうか、またはどこで問題が発生するかを調べていきます。それはプランナーが副選択からの値を認識していないためだと思いますが、_=_演算子を使用すると、インデックススキャンの使用を計画しているはずです。

2
Colin 't Hart

どうですか:

SELECT column FROM table OFFSET (
    SELECT floor(random() * (SELECT count(id) from table))::int
) limit 1;

注意すべき重要な点は、シーケンシャルスキャンを使用しないようにするには、テーブルにインデックスが必要です。追加 explain planをquueryの前に置き、それがどのように実行されるかを確認します。

0
Swav

必要なのは、小さいサンプルパーセンテージを指定して、サンプルサイズを可能な限り「1行」に近づけることです(そうでない場合は整数値であると想定しているようです)。たとえば、1万行のテーブルの場合、select something from table10k tablesample bernoulli (0.02) limit 1を実行します。

小さすぎるサンプルパーセンテージを選択すると、サンプルサイズが1未満になる確率が高くなります。

本当に大きなテーブルの場合、おそらくtablesample system

0
mustaccio