2016-11-25 9 views
0

ウィザードがあります。 次のSQLクエリの奇妙なパフォーマンスで私の頭が壊れています。それは私がテーブルに置かれるデータの量を計算するのに約10秒かかります入れ子になったクエリを最適化して、驚異的なパフォーマンスを避けてください

WITH temp_song_id_with_all_stemmed_words AS 
    (SELECT song_id FROM stemmed_words 
    WHERE stemmed_word IN ('yesterdai','troubl','seem','awai','believ') 
    GROUP BY song_id 
    HAVING COUNT(*)=5) 
SELECT * 
FROM words 
WHERE song_id IN(
    SELECT song_id 
    FROM temp_song_id_with_all_stemmed_words 
) 
ORDER BY song_id, global_position; 

: は、私はSQLコード(PG上で実行されている)を、以下で構成されている次のクエリを持っています。その後、

    一時テーブル内の句「と」置くクエリ自体
  • 内部の句「と」入れ
  • とそれに
  • インデックスを照会: 私はこのクエリを最適化するために、様々なアプローチを試してみました可能なすべての列

しかし、それは役に立たなかった。計算時間はまだ約10秒です(すべてがすでにメモリにキャッシュされていると仮定しても、1分もかかることはありません)

次に、物事は全く異なる振る舞いを構成する部品:

SELECT song_id FROM stemmed_words 
     WHERE stemmed_word IN ('yesterdai','troubl','seem','awai','believ') 
     GROUP BY song_id 
     HAVING COUNT(*)=5 

このクエリはトップスを計算するのに約500ミリ秒かかり、そして3つのidを与え、結果として

を私は外側のクエリを計算するために、これらの結果を使用する場合::

SELECT * 
FROM words 
WHERE song_id IN(337409,328981,304231) 
ORDER BY song_id, global_position; 

それは私がここにボンネットの下に何が起こっは考えているが、私は、適切なSQLオプティマイザは、私が上やった何だろうと想像

を完了するのに約30ミリ秒かかります。

私は以下を参照してください。出力説明を見て:

--UPDATE-- は(冗長、解析)説明入力されただけではなく、私がやる

"Merge Join (cost=20253.29..706336.00 rows=6312654 width=21) (actual time=240731.380..259453.350 rows=356 loops=1)" 
" Output: words.song_id, words.word, words.global_position, words.line_number, words.verse_number" 
" Merge Cond: (words.song_id = temp_song_id_with_all_stemmed_words.song_id)" 
" CTE temp_song_id_with_all_stemmed_words" 
" -> HashAggregate (cost=19799.62..19936.11 rows=13649 width=4) (actual time=43.168..44.916 rows=3 loops=1)" 
"   Output: stemmed_words.song_id" 
"   Group Key: stemmed_words.song_id" 
"   Filter: (count(*) = 5)" 
"   Rows Removed by Filter: 17181" 
"   -> Bitmap Heap Scan on public.stemmed_words (cost=474.02..19714.55 rows=17014 width=4) (actual time=10.254..31.899 rows=21099 loops=1)" 
"    Output: stemmed_words.stemmed_word, stemmed_words.song_id" 
"    Recheck Cond: (stemmed_words.stemmed_word = ANY ('{yesterdai,troubl,seem,awai,believ}'::text[]))" 
"    Heap Blocks: exact=12239" 
"    -> Bitmap Index Scan on stemmed_words_pkey (cost=0.00..469.76 rows=17014 width=0) (actual time=6.052..6.052 rows=21099 loops=1)" 
"      Index Cond: (stemmed_words.stemmed_word = ANY ('{yesterdai,troubl,seem,awai,believ}'::text[]))" 
" -> Index Scan using words_song_id_global_position_idx on public.words (cost=0.44..653025.11 rows=12625308 width=21) (actual time=0.117..257820.366 rows=7860598 loops=1)" 
"  Output: words.song_id, words.word, words.global_position, words.line_number, words.verse_number" 
" -> Sort (cost=316.75..317.25 rows=200 width=4) (actual time=44.953..45.017 rows=274 loops=1)" 
"  Output: temp_song_id_with_all_stemmed_words.song_id" 
"  Sort Key: temp_song_id_with_all_stemmed_words.song_id" 
"  Sort Method: quicksort Memory: 25kB" 
"  -> HashAggregate (cost=307.10..309.10 rows=200 width=4) (actual time=44.928..44.929 rows=3 loops=1)" 
"    Output: temp_song_id_with_all_stemmed_words.song_id" 
"    Group Key: temp_song_id_with_all_stemmed_words.song_id" 
"    -> CTE Scan on temp_song_id_with_all_stemmed_words (cost=0.00..272.98 rows=13649 width=4) (actual time=43.171..44.921 rows=3 loops=1)" 
"     Output: temp_song_id_with_all_stemmed_words.song_id" 
"Planning time: 0.481 ms" 
"Execution time: 259454.102 ms" 

正直なところを説明そこに起こっていることを理解していない...私に中国のように見えます。

私はこれを2つの別々のものに分割するのではなく、1つのクエリとして最適化できると感じています。

  • なぜ現在の形式で完了するのに時間がかかりますか?
  • 私は上記のように2つの別々のものにクエリを分割して最適化できますか?
+1

EXPLAIN ANALYZEの結果を表示できますか? –

+0

あなたの質問と、**両方の**ステートメントの実行計画は、** 'explain(analyze、verbose)' **を使用して生成してください。 [_Formatted_](http://stackoverflow.com/help/formatting)** text **お願い、[スクリーンショットなし](http://meta.stackoverflow.com/questions/285551/why-may-i-not -upload-images-of-code-on-so-ask-a-question/285557#285557) –

+0

CTEが最適化フェンスであるため、Postgresは文全体を最適化するのではなく、内側と外側のクエリを個別に最適化します。 CTEを 'from temp_song_id_with_all_stemmed_words'の代わりに' from(select ...) 'に移動すると、おそらくピクチャが変更されます。 –

答えて

2

ここでの問題は、PostgreSQLがCTE(= WITHクエリ)が返す行数を正しく推定できないことです。

PostgreSQLは13649行を見積もっていますが、正しい数字は3です。

私はの2つの操作の間に一時テーブルを持っている限り、2番目の手法( "with"句を一時テーブルの中に入れてクエリを実行する)で良い結果が期待できます。対処しなければならない多くの価値

+0

あなたのソリューションは魔法のように機能しました。私はまだそれがなぜ働いたのか全く正確に理解していません。その理由は、CTEの結果の行数がわからないため、一時テーブルを分析して回避される非最適化されたフローを開始し、結果を計算するためのより良い計画を実行することです。 – Tal

+1

マージまたはハッシュジョインは、大きなテーブルでうまく機能します。 1つのテーブルが小さい場合は、ネストされたループ結合がよく使用されます。 –

関連する問題