Beim Optimieren von Abfragen sind immer zwei Dinge zu beachten:
- Welche Indizes verwendet werden können (möglicherweise müssen Sie Indizes erstellen)
- Wie die Abfrage geschrieben ist (Möglicherweise müssen Sie die Abfrage ändern, damit der Abfrageoptimierer geeignete Indizes finden und Daten nicht wiederholt lesen kann)
Ein paar Beobachtungen:
-
Sie führen Datumsmanipulationen durch, bevor Sie Ihren Daten beitreten. Als allgemeine Regel verhindert dies, dass ein Abfrageoptimierer einen Index verwendet, selbst wenn er existiert. Sie sollten versuchen, Ihre Ausdrücke so zu schreiben, dass indizierte Spalten unverändert auf einer Seite des Ausdrucks vorhanden sind.
-
Ihre Unterabfragen filtern nach demselben Datumsbereich wie
generate_series
. Dies ist eine Duplizierung und schränkt die Fähigkeit des Optimierers ein, die effizienteste Optimierung auszuwählen. Ich vermute, dass dies möglicherweise zur Verbesserung der Leistung geschrieben wurde, da der Optimser keinen Index für die Datumsspalte verwenden konnte (body_time
)? -
HINWEIS :Wir würden eigentlich sehr gerne einen Index auf
Body.body_time
verwenden -
ORDER BY
innerhalb der Unterabfragen ist bestenfalls redundant. Im schlimmsten Fall könnte es den Abfrageoptimierer zwingen, die Ergebnismenge vor dem Zusammenführen zu sortieren. und das ist nicht unbedingt gut für den Abfrageplan. Bestellen Sie lieber erst ganz am Ende zur endgültigen Anzeige. -
Verwendung von
LEFT JOIN
in Ihren Unterabfragen ist unangemessen. Angenommen, Sie verwenden ANSI-Konventionen fürNULL
Verhalten (und das sollten Sie sein), alle äußeren verbindet sich mitenvelope
würdeenvelope_command=NULL
zurückgeben , und diese würden folglich durch die Bedingungenvelope_command=?
ausgeschlossen . -
Unterabfragen
o
undi
sind bis auf denenvelope_command
fast identisch Wert. Dies zwingt den Optimierer, dieselben zugrunde liegenden Tabellen zweimal zu scannen. Sie können eine Pivot-Tabelle verwenden Technik, um die Daten einmal zu verbinden und die Werte in 2 Spalten aufzuteilen.
Versuchen Sie Folgendes, das die Pivot-Technik verwendet:
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
BEARBEITEN :Von Tom H. vorgeschlagener Filter hinzugefügt.