web-dev-qa-db-ja.com

postgresqlランダム関数がクエリでランダムな順序を使用すると重複した結果を返す

私はWindows 10でpostgresql 11.1-1を使用しています。私のシステムで次のようなクエリを実行すると、ランダムな列ごとに異なる番号が表示されます。

postgres=# SELECT random() as sortOrder, random() as col2;
     sortorder     |       col2
-------------------+-------------------
 0.607938482426107 | 0.121234225574881
(1 row)

しかし、次に示すようにORDER BY句を追加すると、ランダムはすべての行に対して毎回同じ乱数を返します。

postgres=# SELECT random() as sortOrder, random() as col2 
               FROM generate_series(0,1) 
               ORDER BY sortOrder;
     sortorder     |       col2
-------------------+-------------------
 0.100375576410443 | 0.100375576410443
 0.170669795479625 | 0.170669795479625
(2 rows)

これには技術的な理由があると思いますが、この最後のクエリで列ごとに異なるランダムな値を取得することは可能ですか?

編集:元の質問で単純化しすぎた可能性があります。私が試みた実際のクエリは次のようなものでした:

SELECT column1, random(), column2, random()
               FROM table1
               ORDER BY random();

したがって、テーブルクエリと組み合わせることができる答えを持つことも重要です。

1
TMorgan

random()は揮発性であり、ドキュメントは ボラティリティについて

Volatile関数を使用したクエリは、値が必要なevery rowで関数を再評価します。

この定義に基づいて、エンジンは関数を行ごとに1回だけ評価するのが適切であり、同じ行の列ごとに1回ではありません。

この最適化をオフにするには、オプティマイザのクエリを微調整して、2つの列が同じ式を持っているという事実を見落とす可能性があります。例えば:

postgres=> SELECT random()+0 as sortOrder, random() as col2 
               FROM generate_series(0,1) 
               ORDER BY sortOrder;
     sortorder      |        col2         
--------------------+---------------------
 0.0412265718914568 | 0.00992022315040231
  0.841067179106176 |   0.818967831321061
(2 rows)

さて、その単純なソリューションは少し壊れやすいように見えるかもしれません。将来の一部のバージョンでは、オプティマイザは理論的にはこれらの2つの式に再利用できる共通の部分式があることを認識するのに十分に賢くなるかもしれません。

本当に堅牢な解決策は、オプティマイザがどのように難しい関数呼び出しでも異なる関数呼び出しを使用するように強制するために、最初に異なる行の列を生成することです複数の評価を回避しようとします。例えば:

SELECT v1,v2 FROM
(select random() as v1, row_number() over() as r1
 FROM generate_series(0,1)) as s1
JOIN
(select random() as v2, row_number() over() as r2
 FROM generate_series(0,1)) as s2
ON s1.r1=s2.r2
ORDER BY 1;
        v1         |        v2         
-------------------+-------------------
 0.145532250870019 | 0.963712519966066
 0.277611976023763 | 0.252654927782714
(2 rows)
1
Daniel Vérité

DanielVéritéの回答に触発されて、私は別のアプローチを使用することになりました。私が書いたかなり大きなクエリと彼の回答を統合するのに苦労したからです。そのため、次のコードを使用して、true_randomというpostgresql関数を作成しました。

select random() as v1
    FROM generate_series(0,1)

次に、random()のすべてのインスタンスをtrue_random()に置き換えました。これで問題は解決したようです。 (ただし、ダニエルが最初に尋ねた質問に答えたので、私はダニエルにクレジットを与えましたが)。

0
TMorgan