Werden WHERE-Klauseln in der Reihenfolge angewendet, in der sie geschrieben wurden?

36

Ich versuche, eine Abfrage zu optimieren, die in eine große Tabelle (37 Millionen Zeilen) eingeht, und eine Frage zu der Reihenfolge, in der die Operationen in einer Abfrage ausgeführt werden.

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

Werden die WHEREKlauseln für den Datumsbereich vor der Unterabfrage ausgeführt? Ist es eine gute Möglichkeit, die restriktivsten Klauseln an die erste Stelle zu setzen, um große Schleifen für andere Klauseln zu vermeiden und eine schnellere Ausführung zu erreichen?

Die Ausführung von Abfragen nimmt jetzt so viel Zeit in Anspruch.

Jorge Vega Sánchez
quelle

Antworten:

68

Um die Antwort von @ alci zu erläutern:

PostgreSQL ist es egal, in welcher Reihenfolge Sie Dinge schreiben

  • PostgreSQL kümmert sich überhaupt nicht um die Reihenfolge der Einträge in einer WHEREKlausel und wählt Indizes und Ausführungsreihenfolge allein auf der Grundlage der Kosten- und Selektivitätsschätzung.

  • Die Reihenfolge, in der Joins geschrieben werden, wird bis zur Konfiguration ebenfalls ignoriert join_collapse_limit. Wenn es mehr Joins gibt, werden diese in der Reihenfolge ausgeführt, in der sie geschrieben wurden.

  • Unterabfragen können vor oder nach der Abfrage ausgeführt werden, die sie enthält, je nachdem, was am schnellsten ist, solange die Unterabfrage ausgeführt wird, bevor die äußere Abfrage die Informationen tatsächlich benötigt. In der Realität wird die Unterabfrage oft in der Mitte ausgeführt oder mit der äußeren Abfrage verschachtelt.

  • Es gibt keine Garantie, dass PostgreSQL tatsächlich Teile der Abfrage ausführt. Sie können komplett weg optimiert werden. Dies ist wichtig, wenn Sie Funktionen mit Nebenwirkungen aufrufen.

PostgreSQL wandelt Ihre Anfrage um

PostgreSQL transformiert Abfragen stark und behält dabei exakt dieselben Effekte bei, damit sie schneller ausgeführt werden, ohne die Ergebnisse zu ändern.

  • Bedingungen außerhalb einer Unterabfrage können erhalten nach unten gedrückt in die Unterabfrage , so dass sie als Teil der Unterabfrage ausführen nicht , wo man sie in der äußeren Abfrage geschrieben

  • Ausdrücke in der Unterabfrage können bis zur äußeren Abfrage gezogen werden , sodass ihre Ausführung als Teil der äußeren Abfrage erfolgt und nicht dort, wo Sie sie in der Unterabfrage geschrieben haben

  • Die Unterabfrage kann, und oft ist, abgeflacht in eine Verknüpfung auf dem äußeren Tisch. Gleiches gilt für Dinge wie EXISTSund NOT EXISTSAbfragen.

  • Ansichten werden in die Abfrage reduziert, die die Ansicht verwendet

  • SQL-Funktionen werden häufig in die aufrufende Abfrage eingefügt

  • ... und es gibt zahlreiche andere Transformationen, die an Abfragen vorgenommen werden, wie z. B. die Vorbewertung konstanter Ausdrücke, die Dekorrelation einiger Unterabfragen und alle möglichen anderen Planer / Optimierer-Tricks.

Im Allgemeinen kann PostgreSQL Ihre Abfrage massiv transformieren und neu schreiben, bis zu dem Punkt, an dem jede dieser Abfragen:

select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;

select *
from my_table
where not exists (
  select 1
  from other_table
  where other_table.my_table_id = my_table.id
);

select *
from my_table
where my_table.id not in (
  select my_table_id
  from other_table
  where my_table_id is not null
);

wird in der Regel alle genau den gleichen Abfrageplan erzeugen. (Vorausgesetzt, ich habe sowieso keine dummen Fehler gemacht).

Es ist nicht ungewöhnlich, dass Sie versuchen, eine Abfrage nur zu optimieren, um festzustellen, dass der Abfrageplaner die von Ihnen ausgeführten Tricks bereits herausgefunden und sie automatisch angewendet hat. Daher ist die handoptimierte Version nicht besser als das Original.

Einschränkungen

Der Planer / Optimierer ist alles andere als unzulänglich und wird durch die Anforderung begrenzt, absolut sicher zu sein, dass er die Auswirkungen der Abfrage, die verfügbaren Daten, mit denen Entscheidungen getroffen werden können, die implementierten Regeln und die CPU-Zeit nicht ändern kann Es kann sich leisten, über die Optimierungen nachzudenken. Beispielsweise:

  • Der Planer stützt sich auf die Statistiken von ANALYZE(normalerweise über Autovakuum). Wenn diese veraltet sind, kann die Planauswahl schlecht sein.

  • Die Statistiken sind nur Stichproben und können daher aufgrund von Stichprobeneffekten irreführend sein, insbesondere wenn eine zu kleine Stichprobe entnommen wird. Falsche Planentscheidungen können die Folge sein.

  • In der Statistik werden einige Arten von Daten über die Tabelle nicht erfasst, z. B. Korrelationen zwischen Spalten. Dies kann dazu führen, dass der Planer schlechte Entscheidungen trifft, wenn davon ausgegangen wird, dass die Dinge unabhängig sind, wenn sie es nicht sind.

  • Der Planer verwendet Kostenparameter random_page_cost, um die relative Geschwindigkeit verschiedener Vorgänge auf dem jeweiligen System, auf dem er installiert ist, zu ermitteln. Dies sind nur Anleitungen. Wenn sie schlecht liegen, können sie zu schlechten Planentscheidungen führen.

  • Jede Unterabfrage mit einem LIMIToder OFFSETkann nicht abgeflacht oder einem Pullup / Pushdown unterzogen werden. Dies bedeutet jedoch nicht, dass es vor allen Teilen der äußeren Abfrage ausgeführt wird, oder dass es überhaupt ausgeführt wird .

  • CTE-Terme (die Klauseln in einer WITHAbfrage) werden immer vollständig ausgeführt, wenn sie überhaupt ausgeführt werden. Sie können nicht abgeflacht werden, und Begriffe können nicht über die CTE-Begriffsgrenze nach oben oder unten verschoben werden. CTE-Terme werden immer vor der letzten Abfrage ausgeführt. Dieses Verhalten entspricht nicht dem SQL-Standard , ist jedoch so dokumentiert, wie PostgreSQL vorgeht.

  • PostgreSQL ist nur eingeschränkt in der Lage, abfrageübergreifende Optimierungen für fremde Tabellen, security_barrierSichten und bestimmte andere spezielle Beziehungsarten vorzunehmen

  • PostgreSQL integriert weder eine Funktion, die in etwas anderem als normalem SQL geschrieben wurde, noch ein Pullup / Pushdown zwischen dieser Funktion und der äußeren Abfrage.

  • Der Planer / Optimierer ist wirklich dumm von der Auswahl von Ausdrucksindizes und von trivialen Datentypunterschieden zwischen Index und Ausdruck.

Tonnenweise auch.

Ihre Anfrage

Im Falle Ihrer Anfrage:

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

Nichts hindert es daran, mit einem zusätzlichen Satz von Joins zu einer einfacheren Abfrage zusammengefasst zu werden, und das wird es höchstwahrscheinlich sein.

Es wird sich wahrscheinlich herausstellen, wie (offensichtlich ungetestet):

select 1 
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province  
inner join center cr on cr.id_cr = province.id_cr 
where upper(offer.code_status) <> 'A' 
   and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
   and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
   and day.date_day >= '2014-10-01' 
   and day.date_day <= '2015-09-30';

PostgreSQL optimiert dann die Verknüpfungsreihenfolge und die Verknüpfungsmethoden auf der Grundlage der geschätzten Selektivität und Zeilenanzahl sowie der verfügbaren Indizes. Wenn diese die Realität angemessen widerspiegeln, werden die Verknüpfungen ausgeführt und die where-Klausel-Einträge in der Reihenfolge ausgeführt, in der sie am besten sind - häufig werden sie miteinander gemischt, sodass ein Teil davon und ein Teil davon ausgeführt wird und dann zum ersten Teil zurückgekehrt wird , etc.

Wie man sieht, was der Optimierer getan hat

Sie können das SQL, in das PostgreSQL Ihre Abfrage optimiert, nicht sehen, da es das SQL in eine interne Abfragebaumdarstellung konvertiert und diese dann ändert. Sie können den Abfrageplan sichern und mit anderen Abfragen vergleichen.

Es gibt keine Möglichkeit, diesen Abfrageplan oder den internen Planbaum zurück zu SQL zu "deparsen".

http://explain.depesz.com/ hat einen anständigen Helfer für den Abfrageplan. Wenn Sie ganz neu in der Abfrage von Plänen usw. sind (in diesem Fall bin ich erstaunt, dass Sie es so weit durch diesen Beitrag geschafft haben), bietet PgAdmin einen grafischen Abfrageplan-Viewer, der viel weniger Informationen bietet, aber einfacher ist.

Verwandte Lesung:

Pushdown- / Pullup- und Abflachungsfunktionen werden in jeder Version weiter verbessert . PostgreSQL hat in der Regel Recht mit Pull-up / Push-down / Flattening-Entscheidungen, aber nicht immer, so dass Sie gelegentlich einen CTE oder den OFFSET 0Hack (ab) verwenden müssen . Wenn Sie einen solchen Fall finden, melden Sie einen Fehler im Abfrageplaner.


Wenn Sie wirklich sehr daran interessiert sind, können Sie auch die debug_print_plansOption verwenden, um den unformatierten Abfrageplan anzuzeigen, aber ich verspreche, Sie möchten das nicht lesen. Ja wirklich.

Craig Ringer
quelle
Wow ... ziemlich vollständige Antwort :-) Einer der Fälle, in denen ich langsame Pläne mit Postgresql hatte (sowie mit anderen bekannten DB-Engines wie Oracle), sind Korrelationen zwischen Spalten oder mehrfach korrelierte Joins. Es kommt häufig vor, dass verschachtelte Schleifen erstellt werden, wenn man bedenkt, dass es an diesem Punkt des Plans nur wenige Zeilen gibt, obwohl es tatsächlich viele Tausende gibt. Eine Möglichkeit, diese Art von Abfragen zu optimieren, besteht darin, 'enable_nestloop = off' zu setzen. für die Dauer der Abfrage.
Alci
Ich bin auf eine Situation gestoßen, in der v9.5.5 versucht hat, TO_DATE anzuwenden, bevor überprüft wurde, ob es angewendet werden kann, und zwar in einer einfachen 7-where-Klausel-Abfrage. Ordnung war wichtig.
user1133275
@ user1133275 In diesem Fall hat es nur zufällig funktioniert, da die Kalkulationskostenschätzungen gleich waren. PostgreSQL wird möglicherweise noch to_datevor dem Einchecken in einer späteren Version oder aufgrund einer Änderung der Optimierungsstatistik ausgeführt. Um zuverlässig eine Prüfung vor einer Funktion ausführen , die nur nach der Prüfung ausgeführt werden sollen, verwenden Sie eine CASEErklärung ab .
Craig Ringer
Eine der besten Antworten, die ich je auf SO gesehen habe! Daumen hoch, Mann!
62 mkv
Ich habe Situationen erlebt, in denen das Hinzufügen von order byAbfragen die Ausführung von Abfragen wesentlich beschleunigte, als wenn es keine gab order by. Das ist einer der Gründe, warum ich meine Abfragen mit Joins so schreibe, als ob ich sie ausführen lassen wollte - es ist schön, einen großartigen Optimierer zu haben, aber ich denke, es ist nicht ratsam, Ihrem Schicksal ganz auf das Ergebnis zu vertrauen und Abfragen zu schreiben, ohne darüber nachzudenken, wie es may beausgeführt ... Große Antwort !!
Greg0ry
17

SQL ist eine deklarative Sprache: Sie sagen, was Sie wollen, nicht, wie Sie es tun sollen. Das RDBMS wählt die Art der Ausführung der Abfrage aus, den Ausführungsplan.

Früher (vor 5 bis 10 Jahren) hatte die Art und Weise, wie eine Abfrage geschrieben wurde, direkte Auswirkungen auf den Ausführungsplan. Heutzutage verwenden die meisten SQL-Datenbank-Engines ein kostenbasiertes Optimierungsprogramm für die Planung. Das heißt, es werden basierend auf den Statistiken der Datenbankobjekte verschiedene Strategien zum Ausführen der Abfrage ausgewertet und die beste ausgewählt.

Meistens ist es das beste, aber manchmal trifft die DB-Engine falsche Entscheidungen, was zu sehr langsamen Abfragen führt.

alci
quelle
Es sollte beachtet werden, dass bei einigen RDBMS die Abfragereihenfolge immer noch von Bedeutung ist, bei den fortgeschritteneren jedoch alles, was Sie sagen, sowohl in der Praxis als auch in der Theorie zutrifft. Wenn der Abfrageplaner eine schlechte Ausführungsreihenfolge auswählt, sind normalerweise Abfragehinweise verfügbar, um WITH(INDEX(<index>))die Auswahl in eine effizientere Richtung zu lenken (z. B. in MSSQL, um die Auswahl des Index für einen bestimmten Join zu erzwingen).
David Spillett
Die Frage ist, ob date_daytatsächlich ein Index für existiert. Wenn es keine gibt, hat der Optimierer nicht viele Pläne zum Vergleichen.
jkavalik