Selalu ada 2 hal yang perlu dipertimbangkan saat mengoptimalkan kueri:
- Indeks apa yang dapat digunakan (Anda mungkin perlu membuat indeks)
- Bagaimana kueri ditulis (Anda mungkin perlu mengubah kueri agar pengoptimal kueri dapat menemukan indeks yang sesuai, dan tidak membaca ulang data secara berlebihan)
Beberapa pengamatan:
-
Anda melakukan manipulasi tanggal sebelum bergabung dengan teman kencan Anda. Sebagai aturan umum, ini akan mencegah pengoptimal kueri menggunakan indeks meskipun indeks itu ada. Anda harus mencoba untuk menulis ekspresi Anda sedemikian rupa sehingga kolom yang diindeks tidak berubah di satu sisi ekspresi.
-
Subkueri Anda difilter ke rentang tanggal yang sama dengan
generate_series
. Ini adalah duplikasi, dan membatasi kemampuan pengoptimal untuk memilih pengoptimalan yang paling efisien. Saya menduga itu mungkin ditulis untuk meningkatkan kinerja karena pengoptimal tidak dapat menggunakan indeks pada kolom tanggal (body_time
)? -
CATATAN :Kami sebenarnya sangat ingin menggunakan indeks pada
Body.body_time
-
ORDER BY
dalam subqueries paling-paling berlebihan. Paling buruk itu bisa memaksa pengoptimal kueri untuk mengurutkan hasil yang ditetapkan sebelum bergabung; dan itu belum tentu bagus untuk rencana kueri. Alih-alih hanya menerapkan pemesanan tepat di akhir untuk tampilan akhir. -
Penggunaan
LEFT JOIN
di subkueri Anda tidak pantas. Dengan asumsi Anda menggunakan konvensi ANSI untukNULL
perilaku (dan Anda seharusnya), luar bergabung keenvelope
akan mengembalikanenvelope_command=NULL
, dan ini akibatnya akan dikecualikan oleh kondisienvelope_command=?
. -
Subkueri
o
dani
hampir identik kecuali untukenvelope_command
nilai. Ini memaksa pengoptimal untuk memindai tabel dasar yang sama dua kali. Anda dapat menggunakan tabel pivot teknik untuk menggabungkan data satu kali, dan membagi nilai menjadi 2 kolom.
Coba yang berikut ini yang menggunakan teknik pivot:
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
EDIT :Menambahkan filter yang disarankan oleh Tom H.