2016-09-21 1 views
1

WHERE句random() > 0.5を使用して、データのランダムな部分集合を選択することがよくあります。今私は、サブクエリでセット返された関数を使用するとき、私は全体のセットを取得するか、またはnone(WHERE random()> 0.5句が、セットが生成される前にと解釈されることを意味する) は、例えば:次のクエリは、アカウントにセット全体を取るんのでサブクエリでrandom()を返す関数の動作が一貫しない

SELECT num 
FROM (
    SELECT unnest(Array[1,2,3,4,5,6,7,8,9,10]) num 
) AS foo 
WHERE random() > 0.5; 

これは矛盾しているようだ:

SELECT num 
FROM (
    SELECT unnest(Array[1,2,3,4,5,6,7,8,9,10]) num 
) AS foo 
WHERE random() > 0.1 * num; 

私は、これは矛盾しているか、それが意味をなすんので修正でしょうか?

注:

  • )(ランダム別にテストするために別の関数を見つけることができなかったが、おそらく私もgenerate_seriesで

+0

あなたは 'EXPLAIN'上で実行することができますクエリ。その実行計画が異なるのは、最初のクエリで毎回 'random()'を実行しないのが最適だからですが、それは2番目のクエリで実行する必要があります。 – GavinCattell

+0

2番目の文には、サブクエリのすべての結果と比較してランダムな値が含まれます。最初のものはあなたのサブクエリとは関係ありません。これは0.5に比べてランダムに生成された単一の数値です。 –

答えて

1

確かにpostgresメーリングリストは良い反応を示していて、おそらくバグです。

これはトム・レーンからの回避策を含め答え、次のとおりです。


うーん、私は、これは、オプティマイザのバグだと思います。

SELECT * FROM unnest(ARRAY[1,2,3,4,5,6,7,8,9,10]) WHERE random() > 0.5; 

は(とない)アンネストにより、すべての行の出力のためのWHEREを再評価する必要があります():2つの正当な行動ここ があります。それはTARGETLISTで 集合を返す関数の拡張の前に起こるので、

SELECT unnest(ARRAY[1,2,3,4,5,6,7,8,9,10]) WHERE random() > 0.5; 

は、WHERE一度だけ評価する必要があります。 (あなたはOracleユーザーだと あなたは「デュアルFROM」暗黙のを持つものとして、このクエリを想像した場合は、WHEREが FROM句から出てくる単一の行のために評価されるべきである。)

場合、あなたは」ここでは、外側の クエリのWHEREの配置を考えれば、内部クエリの が出てくる各行に対して評価されると確信しています。しかし、オプティマイザはWHERE 節を押し下げてサブ選択のWHEREにすることができると判断しています。 のケースでは合法ですが、サブ選択の ターゲットリストにSRFがある場合はそうではありません。これは、2つのクエリ間の変更に類似して、SRFの前にWHEREが発生するようにプッシュするためです私が書いた。

私は、これを既存のリリースで変更することを躊躇しています。以前の苦情のうち が不足しているため、 というクエリを壊して、人々を幸せにするよりも期待通りに動作しているようです。しかし、 をv10以降で変更することができます。特に、 SRF-in-tlistの動作のいくつかのコーナーケースの変更が行われているためです。一方

あなたは 万能最適化のフェンスを挿入することにより、望むように、あなたは、サブ選択して「オフセット0」動作するように強制することができます:

=# SELECT num FROM (
    SELECT unnest(Array[1,2,3,4,5,6,7,8,9,10]) num OFFSET 0) AS foo WHERE random() > 0.5; 
num 
----- 
    1 
    4 
    7 
    9 
(4 rows) 
3

をテストしたいくつかの

  • あり最初のクエリでは、where句の式は、selectの列に関連しないため、1回実行されます。

    第2のケースで
    Result (cost=0.01..0.51 rows=100 width=0) (actual time=0.017..0.021 rows=10 loops=1) 
        One-Time Filter: (random() > '0.5'::double precision) 
    Planning time: 0.156 ms 
    Execution time: 0.058 ms 
    

    where式は、列に依存します:

    Subquery Scan on foo (cost=0.00..2.76 rows=33 width=4) (actual time=0.052..0.083 rows=5 loops=1) 
        Filter: (random() > ((0.1 * (foo.num)::numeric))::double precision) 
        Rows Removed by Filter: 5 
        -> Result (cost=0.00..0.51 rows=100 width=0) (actual time=0.017..0.022 rows=10 loops=1) 
    Planning time: 0.119 ms 
    Execution time: 0.137 ms 
    
  • +0

    技術的に、私は与えられた答えに同意し、私は起こっていたと考えていることを強調します。しかし、どういうわけか私は一貫性に満足していませんが、言葉で表現するのは難しいです。私のポイントは、それはユースケースの観点から感心していますか? (言い換えれば教えてください) – tilt

    +1

    私はあなたの意見を見ます。ただし、 'order by expression_not_related_to_results'は特殊なケースであることに注意してください。これはSQLロジックのトリックです。 – klin

    2

    そうだね、これは非常に矛盾したようです。

    ここで重要な点は、random()VOLATILEであることです。これは、理論上、クエリプランナがこの関数の呼び出しをすべて最適化してはならないことを意味します。

    興味深いことに、これはSELECT * FROM f()ではなく、SELECT f()でセットリターン関数を呼び出すときにのみ発生します。これはバグか、単に既知の制限であればこのような動作が期待されている同様の例があるように私は、知らない

    SELECT num 
    FROM (
        SELECT * FROM unnest(Array[1,2,3,4,5,6,7,8,9,10]) num 
    ) AS foo 
    WHERE random() > 0.5; 
    

    :このクエリは、期待される結果を提供します。たとえば、次の比較:あなたはここで決定的な答えを得られない場合は

    SELECT random() FROM generate_series(1,10);   -- 10 random numbers 
    SELECT (SELECT random()) FROM generate_series(1,10); -- 10 copies of the same random number 
    

    、あなたが見ている行動が意図されている場合はPostgres mailing listを依頼する場合があります。

    関連する問題