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 WHERE
Klausel 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 EXISTS
und NOT EXISTS
Abfragen.
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 LIMIT
oder OFFSET
kann 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 WITH
Abfrage) 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_barrier
Sichten 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 0
Hack (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_plans
Option verwenden, um den unformatierten Abfrageplan anzuzeigen, aber ich verspreche, Sie möchten das nicht lesen. Ja wirklich.
to_date
vor 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 eineCASE
Erklärung ab .order by
Abfragen die Ausführung von Abfragen wesentlich beschleunigte, als wenn es keine gaborder 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 esmay be
ausgeführt ... Große Antwort !!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.
quelle
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).date_day
tatsächlich ein Index für existiert. Wenn es keine gibt, hat der Optimierer nicht viele Pläne zum Vergleichen.