PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Optimieren mehrerer Verknüpfungen

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ür NULL Verhalten (und das sollten Sie sein), alle äußeren verbindet sich mit envelope würde envelope_command=NULL zurückgeben , und diese würden folglich durch die Bedingung envelope_command=? ausgeschlossen .

  • Unterabfragen o und i sind bis auf den envelope_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.