クエリを最適化する際には、常に2つの考慮事項があります。
- 使用できるインデックス(インデックスの作成が必要になる場合があります)
- クエリの記述方法(クエリオプティマイザーが適切なインデックスを見つけられるようにし、データを重複して再読み込みしないようにするには、クエリを変更する必要がある場合があります)
いくつかの所見:
-
日付を結合する前に、日付操作を実行しています。原則として、これにより、クエリオプティマイザがインデックスが存在する場合でもインデックスを使用できなくなります。式の片側にインデックス付きの列が変更されずに存在するように、式を作成するようにしてください。
-
サブクエリは、
generate_series
と同じ日付範囲にフィルタリングされています 。これは重複であり、最も効率的な最適化を選択するオプティマーの能力を制限します。オプティマイザーが日付列(body_time
)のインデックスを使用できなかったため、パフォーマンスを向上させるために書き込まれた可能性があります。 )? -
注 :実際には、
Body.body_time
のインデックスを使用したいと思います。 -
ORDER BY
サブクエリ内はせいぜい冗長です。最悪の場合、クエリオプティマイザーが結合する前に結果セットをソートするように強制される可能性があります。これは、クエリプランにとって必ずしも良いとは限りません。むしろ、最終的な表示のために最後に注文を適用するだけです。 -
LEFT JOIN
の使用 あなたのサブクエリでは不適切です。NULL
にANSI規則を使用していると仮定します 振る舞い(そしてあなたはそうあるべきです)、どんな外側envelope
に参加しますenvelope_command=NULL
を返します 、したがって、これらは条件envelope_command=?
によって除外されます。 。 -
サブクエリ
o
およびi
envelope_command
を除いてほぼ同じです 価値。これにより、オプティマイザーは同じ基になるテーブルを2回スキャンするように強制されます。 ピボットテーブルを使用できます データに1回結合し、値を2列に分割する手法。
ピボット手法を使用する次の方法を試してください。
SELECT p.period,
/*The pivot technique in action...*/
SUM(
CASE WHEN envelope_command = 1 THEN body_size
ELSE 0
END) AS Outbound,
SUM(
CASE WHEN envelope_command = 2 THEN body_size
ELSE 0
END) AS Inbound
FROM (
SELECT date '2009-10-01' + s.day AS period
FROM generate_series(0, date '2009-10-31' - date '2009-10-01') AS s(day)
) AS p
/*The left JOIN is justified to ensure ALL generated dates are returned
Also: it joins to a subquery, else the JOIN to envelope _could_ exclude some generated dates*/
LEFT OUTER JOIN (
SELECT b.body_size,
b.body_time,
e.envelope_command
FROM body AS b
INNER JOIN envelope e
ON e.message_id = b.message_id
WHERE envelope_command IN (1, 2)
) d
/*The expressions below allow the optimser to use an index on body_time if
the statistics indicate it would be beneficial*/
ON d.body_time >= p.period
AND d.body_time < p.period + INTERVAL '1 DAY'
GROUP BY p.Period
ORDER BY p.Period
編集 :TomHによって提案されたフィルターを追加しました。