2

とPostgresSQLのCTE /サブクエリ===== =====更新generate_seriesのパフォーマンスの問題

により明確化を求めているいくつかの初期の質問に、ここではこののは本当に簡単なバージョンがあります。私たちがここにいる

WITH my_var AS ( 
    SELECT date '2016-01-01' as a_date   
    , generate_series(1, 40) as numbers  
) 
Select generate_series(1, 100000) as numbers, my_var.a_date from my_var 

execution time: 16201ms 

"CTE Scan on my_var (cost=5.01..5022.51 rows=1000000 width=4)" 
" CTE my_var" 
" -> Result (cost=0.00..5.01 rows=1000 width=0)" 

ポイントでgenerate_seriesコメントを解除した場合

WITH my_var AS ( 
    SELECT date '2016-01-01' as a_date   
    --, generate_series(1, 40) as numbers  
    ) 
Select generate_series(1, 100000) as numbers, my_var.a_date from my_var 

execution time: 411ms 

"CTE Scan on my_var (cost=0.01..5.03 rows=1000 width=4)" 
" CTE my_var" 
" -> Result (cost=0.00..0.01 rows=1 width=0)" 

は今generate_series(1、40)は1回だけ実行されるようになっている場合、なぜそれがそう取るんですクエリが完了するまで長い間。この場合、私はメインクエリで '数字'セットを使用していないし、それでも完了するまでに時間がかかりました。

=====オリジナルお問い合わせ=====

私は、サブクエリおよび/またはCTEを使用してのPostgresSQL 9.xので興味深いパフォーマンスの問題に遭遇しました。

... CTE /サブクエリやgenerate_series関数の使用を「バグ」またはユーザー(つまりMe)が理解できるかどうかは完全に正直ではありません。

私は、CTEを使用して高度な、より長いクエリを書いてきました。私は、すべての追加クエリをフィルタリングするマスターCTEに日付などの静的変数を配置したテクニックを使用してきました。この考え方は、さまざまなパラメータで実行する必要がある場合に、長いクエリで1つの変更を複数の変更よりもむしろ1つ作成することです。

この例は次のとおりです。

WITH dates AS ( 
    SELECT 
     date '2013-01-01' AS start_date, 
     date_trunc('month', current_date) AS end_date 
) 
SELECT * from dates, sometable where somedate between start_date and end_date 

execution time: ~650ms 

だから、それはCTEが一度に実行されていることを私の理解ではあるが、パフォーマンスの問題に実行した後に、これは何が起こっているか明確ではありません。例えば、私はCTEを変更した場合generate_series含める:これによって(遅く何千回)といくつかの深刻なパフォーマンスの問題に

WITH dates AS ( 
    SELECT 
     date '2013-01-01' AS start_date, 
     date_trunc('month', current_date) AS end_date, 
     generate_series(1, 10) AS somelist 
) 
SELECT * from dates, sometable where somedate between start_date and end_date 
    and myval in (somelist) 

execution time: ~23000ms 

を、私は、最初の「generate_series somelistを割り当てる)(generate_seriesを思いましたメインクエリでsometable内のすべての行のサブクエリとして実行されます。だから私は、クエリを変更し、これを確認するには、次のとおりです。

WITH dates AS ( 
    SELECT 
     date '2013-01-01' AS start_date, 
     date_trunc('month', current_date) AS end_date--, 
     --generate_series(1, 10) AS somelist 
) 
SELECT * from dates, sometable where somedate between start_date and end_date 
    and myval in (generate_series(1, 10)) 


execution time: ~700ms 

を私の驚きに、これは比較的高速(とのみ10%遅くなる)でした。サブクエリとしてのgenerate_seriesは明らかに問題ではありません。

その後、元のクエリに戻り、generate_seriesを追加しましたが、メインクエリでこれを使用しませんでした。ここにその質問があります。

WITH dates AS ( 
    SELECT 
     date '2013-01-01' AS start_date, 
     date_trunc('month', current_date) AS end_date, 
     generate_series(1, 10) AS somelist 
) 
SELECT * from dates, sometable where somedate between start_date and end_date 

execution time: ~23000ms 

これは明らかに喫煙銃です...しかし、私は何が本当に起こっているのか、その理由については全く知らないです。要約すると、CTEまたはサブクエリ内でgenerate_seriesを使用すると、(結果が使用されていなくても)膨大な量の時間/リソースを消費しています。 Postgres v9.3とv9.5の両方で同じ結果が得られます。私は約1400万行を実行している私はテーブルです。結果セットはわずか約275Kです。

私はこの時点では無知です、誰にも理論がありますか? (...それともバグですか?)

-D

+0

興味深い質問ですが、インデックスのような 'sometable'についての詳細情報を提供する必要があります。また、すべてのクエリの 'explain analyze'をインクルードして、rdbmsを実行していることを理解し、各クエリを比較すると良いでしょう。 –

+0

クエリにテーブルエイリアスを追加し、カラム名を参照するときに使用してください。それは完全に漠然とした表現であり、それはカルテシアンの製品であってもかまわない。 'SELECT * from dates、sometable'の代わりにJOIN構文を使用して読みやすくすることができます。 – wildplasser

+0

@元 - 私の指摘は、インデックスについては関係ありません。実際のテーブルに接続する必要はありません。私はこれを示すためのアップデートを投稿します。 – user2259963

答えて

0

実験(彼らは単に追加のスカラ定数であるので、私は、日付を除外)

EXPLAIN 
WITH my_cte_b AS (
     SELECT generate_series(1, 40) as b_number 
     ) 
, my_cte_c AS (
     SELECT generate_series(1, 1000) AS c_number 
     ) 
Select 
     my_cte_b.b_number 
     , my_cte_c.c_number 
FROM my_cte_b 
JOIN my_cte_c ON (1=1) 
     ; 

結果:

        QUERY PLAN        
------------------------------------------------------------------ 
Nested Loop (cost=5.01..10020.01 rows=1000000 width=8) 
    CTE my_cte_b 
    -> Result (cost=0.00..2.50 rows=1000 width=0) 
    CTE my_cte_c 
    -> Result (cost=0.00..2.50 rows=1000 width=0) 
    -> CTE Scan on my_cte_b (cost=0.00..10.00 rows=1000 width=4) 
    -> CTE Scan on my_cte_c (cost=0.00..10.00 rows=1000 width=4) 
(7 rows) 

しかしEXPLAIN ANALYZEができます正しい結果:

----------------------------- 
Nested Loop (cost=5.01..10020.01 rows=1000000 width=8) (actual time=0.029..8.953 rows=40000 loops=1) 
    CTE my_cte_b 
    -> Result (cost=0.00..2.50 rows=1000 width=0) (actual time=0.013..0.019 rows=40 loops=1) 
    CTE my_cte_c 
    -> Result (cost=0.00..2.50 rows=1000 width=0) (actual time=0.002..0.095 rows=1000 loops=1) 
    -> CTE Scan on my_cte_b (cost=0.00..10.00 rows=1000 width=4) (actual time=0.021..0.040 rows=40 loops=1) 
    -> CTE Scan on my_cte_c (cost=0.00..10.00 rows=1000 width=4) (actual time=0.000..0.104 rows=1000 loops=40) 
Planning time: 0.042 ms 
Execution time: 25.206 ms 
(9 rows) 

だから、問題は推定ではなく実行中であるように見えます。ボーナスとして


:あなたできヒント(または:愚か者)のCTEにLIMIT xxを置くことによってプランナー:


EXPLAIN ANALYZE 
WITH my_cte_b AS (
     SELECT generate_series(1, 40) as b_number 
     LIMIT 40 
     ) 
, my_cte_c AS (
     SELECT generate_series(1, 1000) AS c_number 
     LIMIT 10000 
     ) 
Select 
     my_cte_b.b_number 
     , my_cte_c.c_number 
FROM my_cte_b 
JOIN my_cte_c ON (1=1) 
     ; 

                QUERY PLAN             
---------------------------------------------------------------------------------------------------------------- 
Nested Loop (cost=2.60..408.00 rows=40000 width=8) (actual time=0.019..9.347 rows=40000 loops=1) 
    CTE my_cte_b 
    -> Limit (cost=0.00..0.10 rows=40 width=0) (actual time=0.008..0.018 rows=40 loops=1) 
      -> Result (cost=0.00..2.50 rows=1000 width=0) (actual time=0.006..0.013 rows=40 loops=1) 
    CTE my_cte_c 
    -> Limit (cost=0.00..2.50 rows=1000 width=0) (actual time=0.002..0.241 rows=1000 loops=1) 
      -> Result (cost=0.00..2.50 rows=1000 width=0) (actual time=0.002..0.134 rows=1000 loops=1) 
    -> CTE Scan on my_cte_b (cost=0.00..0.40 rows=40 width=4) (actual time=0.012..0.036 rows=40 loops=1) 
    -> CTE Scan on my_cte_c (cost=0.00..10.00 rows=1000 width=4) (actual time=0.000..0.112 rows=1000 loops=40) 
Planning time: 0.096 ms 
Execution time: 10.693 ms 
(11 rows) 

私の結論を:プランナーが持っていませんCTEに関する統計情報(物理テーブルへの参照は含まれません)は、推測(1000)に過ぎません。この推測は、CTEの内部にLIMITを置くことによって却下される可能性があります。

関連する問題