Postgresql 9.2 システムで、通常の形式で約 20 秒かかるクエリがありますが、CTE を使用すると 120 ミリ秒しかかかりません。
簡潔にするために、両方のクエリを単純化しました。
通常の形式は次のとおりです (約 20 秒かかります)。
SELECT *
FROM tableA
WHERE (columna = 1 OR columnb = 2) AND
atype = 35 AND
aid IN (1, 2, 3)
ORDER BY modified_at DESC
LIMIT 25;
このクエリの説明は次のとおりです。 http://explain.depesz.com/s/2v8
CTE フォーム (約 120ms):
WITH raw AS (
SELECT *
FROM tableA
WHERE (columna = 1 OR columnb = 2) AND
atype = 35 AND
aid IN (1, 2, 3)
)
SELECT *
FROM raw
ORDER BY modified_at DESC
LIMIT 25;
CTE の説明は次のとおりです: http://explain.depesz.com/s/uxy
をクエリの外側に移動するだけORDER BY
で、コストが 99% 削減されます。
2 つの質問があります。1) CTE を使用せずに最初のクエリを作成し、論理的に同等のパフォーマンスを実現する方法はありますか?2) パフォーマンスのこの違いは、プランナーがフェッチ方法を決定する方法について何を示していますか?データ?
上記の質問に関して、最初のクエリのパフォーマンスを向上させるのに役立つ追加の統計情報やその他のプランナーのヒントはありますか?
編集:制限を取り除くと、クエリは逆方向のインデックス スキャンではなくヒープ スキャンを使用するようになります。クエリがなければ、LIMIT
40 ミリ秒で完了します。
の効果を見た後、 、 などLIMIT
で試しました。クエリは、使用すると 100 ミリ秒未満で実行され、 > 1では 10 秒以上実行されます。LIMIT 1
LIMIT 2
LIMIT 1
LIMIT
これについてもう少し考えた後、質問 2 は、なぜプランナーが逆方向のインデックス スキャンを使用し、別の論理的に同等のケースでビットマップ ヒープ スキャン + ソートを使用するのかという問題に要約されます。そして、プランナーが両方のケースで効率的な計画を使用するのをどのように「助ける」ことができますか?
更新: 最も包括的で役立つ Craig の回答を受け入れました。私が最終的に問題を解決した方法は、論理的には同等ではありませんが、実質的に同等であるクエリを使用することでした。問題の根底にあるのは、modified_at のインデックスを逆方向にスキャンすることでした。これは良い考えではなかったことをプランナーに知らせるために、フォームの述語を追加しWHERE modified_at >= NOW() - INTERVAL '1 year'
ます。これには、アプリケーション用の十分なデータが含まれていましたが、プランナーが逆方向のインデックス スキャン パスをたどることができませんでした。
これは、サブクエリまたは CTE のいずれかを使用してクエリを書き直す必要をなくす、はるかに影響の少ないソリューションでした。YMMV。