Warum werden Abfragen so analysiert, dass die Verwendung von Spaltenaliasnamen in den meisten Klauseln nicht zulässig ist?

16

Beim Versuch, eine Abfrage zu schreiben, habe ich herausgefunden (auf die harte Tour), dass SQL Server WHEREs in einer Abfrage lange vor dem Parsen der SELECTs analysiert, wenn eine Abfrage ausgeführt wird.

Die MSDN-Dokumentation besagt , dass die allgemeine logische Parsing-Reihenfolge so ist, dass SELECT fast zuletzt analysiert wird (was zu Fehlern "Kein Objekt [Alias]" führt, wenn versucht wird, einen Spaltenalias in anderen Klauseln zu verwenden). Es gab sogar einen Vorschlag , die Verwendung von Aliasnamen überall zuzulassen, der vom Microsoft-Team unter Berufung auf Probleme mit der Einhaltung von ANSI-Standards abgeschossen wurde (was darauf hindeutet, dass dieses Verhalten Teil des ANSI-Standards ist).

Als Programmierer (kein DBA) fand ich dieses Verhalten etwas verwirrend, da es meines Erachtens den Zweck von Spaltenaliasen weitgehend zunichte macht (oder zumindest Spaltenaliasen erheblich leistungsfähiger gemacht werden könnten, wenn sie es wären) geparst zuvor in der Abfrageausführung), da der einzige Ort, an dem Sie die Aliase tatsächlich verwenden können, ORDER BY ist. Als Programmierer scheint es eine große Chance zu versäumen, Abfragen leistungsfähiger, praktischer und trockener zu machen.

Es sieht so aus, als wäre es ein so krasses Problem, dass es naheliegend ist, zu entscheiden, dass Spalten-Aliase nur in SELECT und ORDER BY zulässig sind. Aber was sind diese Gründe?

Shauna
quelle

Antworten:

19

Zusammenfassung

Es gibt keinen logischen Grund, warum dies nicht möglich ist, aber der Nutzen ist gering und es gibt einige Fallstricke, die möglicherweise nicht sofort erkennbar sind.

Forschungsergebnisse

Ich habe ein paar Nachforschungen angestellt und gute Informationen gefunden. Das Folgende ist ein direktes Zitat aus einer zuverlässigen Primärquelle (die anonym bleiben möchte) am 2012-08-09 17:49 GMT:

Als SQL zum ersten Mal erfunden wurde, hatte es keine Aliase in der SELECT-Klausel. Dies war ein schwerwiegender Mangel, der behoben wurde, als die Sprache 1986 von ANSI standardisiert wurde.

Die Sprache sollte "nicht prozedural" sein, dh die gewünschten Daten beschreiben, ohne anzugeben, wie sie gefunden werden sollen. Soweit ich weiß, gibt es keinen Grund, warum eine SQL-Implementierung nicht die gesamte Abfrage analysieren kann, bevor sie verarbeitet wird. Außerdem können Aliase überall definiert und verwendet werden. Zum Beispiel sehe ich keinen Grund, warum die folgende Abfrage nicht gültig sein sollte:

select name, salary + bonus as pay
from employee
where pay > 100000

Obwohl ich dies für eine vernünftige Abfrage halte, können einige SQL-basierte Systeme aus Gründen der Implementierung Einschränkungen für die Verwendung von Aliasnamen einführen. Ich bin nicht überrascht zu hören, dass SQL Server dies tut.

Ich bin an weiteren Forschungen zum SQL-86-Standard interessiert und daran, warum moderne DBMS die Wiederverwendung von Aliasen nicht unterstützen, aber noch nicht die Zeit hatten, damit sehr weit zu kommen. Für den Anfang weiß ich nicht, wo ich die Dokumentation bekommen soll oder wie ich herausfinden kann, wer genau das Komitee gebildet hat. Kann mir jemand helfen? Ich möchte auch mehr über das ursprüngliche Sybase-Produkt erfahren, von dem SQL Server stammt.

Aufgrund dieser Nachforschungen und einiger weiterer Überlegungen habe ich den Verdacht, dass die Verwendung von Aliasnamen in anderen Klauseln, obwohl dies durchaus möglich ist, für DBMS-Hersteller im Vergleich zu anderen Sprachfunktionen noch nie so hohe Priorität hatte. Da dies kein allzu großes Hindernis darstellt, ist es nicht optimal, wenn der Query-Writer dies leicht umgeht und sich mehr Mühe gibt, als andere Verbesserungen. Darüber hinaus wäre es proprietär, da es offensichtlich nicht Teil des SQL-Standards ist (obwohl ich sicher darauf warte, mehr darüber herauszufinden) und daher eine geringfügige Verbesserung wäre, die die SQL-Kompatibilität zwischen DBMS-Systemen unterbricht. Im Vergleich dazu CROSS APPLY(was eigentlich nichts anderes als eine abgeleitete Tabelle ist, die äußere Verweise zulässt) handelt es sich um eine enorme Veränderung, die, obwohl proprietär, eine unglaubliche Ausdruckskraft bietet, die auf andere Weise nicht einfach zu realisieren ist.

Probleme mit der Verwendung von Aliasen überall

Wenn Sie zulassen, dass SELECT-Elemente in die WHERE-Klausel aufgenommen werden, können Sie nicht nur die Komplexität der Abfrage (und damit die Komplexität, einen guten Ausführungsplan zu finden) aufklären, sondern auch völlig unlogische Dinge. Versuchen:

SELECT X + 5 Y FROM MyTable WHERE Y = X

Was passiert, wenn MyTable bereits eine Spalte Y enthält, auf die sich die WHERE-Klausel bezieht? Die Lösung besteht darin, einen CTE oder eine abgeleitete Tabelle zu verwenden, was in den meisten Fällen keine zusätzlichen Kosten verursacht, aber dasselbe Endergebnis erzielt. CTEs und abgeleitete Tabellen erzwingen zumindest die Auflösung von Mehrdeutigkeiten, indem ein Alias ​​nur einmal verwendet werden darf.

Es ist auch sehr sinnvoll, in der FROM-Klausel keine Aliase zu verwenden. Das kannst du nicht machen:

SELECT
   T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
FROM
   Table1 T
   INNER JOIN Table2 T2
      ON T2.ID = CalcID
   INNER JOIN Table3 T3
      ON T2.ID = T3.ID

Das ist ein Zirkelverweis (in dem Sinne, dass T2 sich heimlich auf einen Wert aus T3 bezieht, bevor diese Tabelle in der JOIN-Liste aufgeführt wurde) und verdammt schwer zu sehen. Wie wäre es mit diesem:

INSERT dbo.FinalTransaction
SELECT
   newid() FinalTransactionGUID,
   'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
   T.*
FROM
   dbo.MyTable T

Wie viel möchten Sie wetten, dass die newid () - Funktion zweimal in den Ausführungsplan aufgenommen wird, sodass die beiden Spalten völlig unerwartet unterschiedliche Werte anzeigen? Was ist, wenn die obige Abfrage N Ebenen tief in CTEs oder abgeleiteten Tabellen verwendet wird. Ich garantiere, dass das Problem schlimmer ist, als Sie sich vorstellen können. Es gibt bereits schwerwiegende Inkonsistenzprobleme, wenn Dinge nur einmal ausgewertet werden oder zu welchem ​​Zeitpunkt in einem Abfrageplan, und Microsoft hat angegeben, dass dies nicht behoben werden kannEinige davon, weil sie die Abfrage-Algebra korrekt ausdrücken. Wenn Sie unerwartete Ergebnisse erhalten, teilen Sie die Abfrage in Teile auf. Zulassen von verketteten Referenzen, Erkennen von Zirkelreferenzen durch möglicherweise sehr lange solche Ketten - dies sind recht knifflige Probleme. Führen Sie Parallelität ein und Sie haben einen Albtraum im Entstehen.

Hinweis: Die Verwendung des Alias ​​in WHERE oder GROUP BY hat keinen Einfluss auf die Probleme mit Funktionen wie newid () oder rand ().

Eine SQL Server-Methode zum Erstellen wiederverwendbarer Ausdrücke

CROSS APPLY / OUTER APPLY ist eine Möglichkeit in SQL Server, Ausdrücke zu erstellen, die an einer anderen Stelle in der Abfrage verwendet werden können (nur nicht früher in der FROM-Klausel):

SELECT
   X.CalcID
FROM
   Table1 T
   INNER JOIN Table3 T3
      ON T.ID = T3.ID
   CROSS APPLY (
      SELECT
         T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
   ) X
   INNER JOIN Table2 T2
      ON T2.ID = X.CalcID

Das macht zwei Dinge:

  1. Lässt alle Ausdrücke in CROSS APPLY einen "Namespace" (ein Tabellenalias, hier X) erhalten und in diesem Namespace eindeutig sein.
  2. Verdeutlicht überall, dass CalcID nicht nur von X stammt, sondern auch, warum Sie beim Verknüpfen der Tabellen T1 und T3 nichts von X verwenden können, da X noch nicht eingeführt wurde.

Eigentlich mag ich CROSS APPLY. Es ist mein treuer Freund geworden und ich benutze es die ganze Zeit. Benötigen Sie einen unvollständigen UNPIVOT (für den PIVOT / UNPIVOT oder UNPIVOT / PIVOT mit nativer Syntax erforderlich wäre)? Fertig mit CROSS APPLY. Benötigen Sie einen berechneten Wert, der viele Male wiederverwendet wird? Erledigt. Müssen Sie die Ausführungsreihenfolge für Anrufe über einen Verbindungsserver streng erzwingen? Fertig mit einer schreienden Geschwindigkeitsverbesserung. Benötigen Sie nur einen Zeilentyp, der in zwei Zeilen aufgeteilt ist, oder zusätzliche Bedingungen? Erledigt.

Zumindest in DBMS SQL Server 2005 und höher gibt es also keinen Grund mehr, sich zu beschweren: Mit CROSS APPLY trocknen Sie so, wie Sie es möchten.

ErikE
quelle
14

Ich kann Ihnen die genauen Gründe nicht nennen, aber ich sage Ihnen, dass es Problemumgehungen gibt, um Ausdrücke zu wiederholen, beispielsweise mithilfe von CTEs, Unterabfragen, abgeleiteten Tabellen usw., um Wiederholungen zu vermeiden.

Wenn Sie eine Abfrage mit einem wiederholten Ausdruck anzeigen, können wir Ihnen wahrscheinlich zeigen, wie Sie ihn neu schreiben, sodass der Ausdruck nur einmal aufgeführt wird. Dies verringert jedoch nur die Komplexität beim Schreiben / Lesen der Abfrage, und es ist unwahrscheinlich, dass sich viel an der Effizienz ändert. SQL Server erkennt im Allgemeinen sehr gut, dass Ausdrücke wiederholt werden, und führt diese Arbeit nicht zweimal aus. Es gibt Ausnahmen, die in die andere Richtung gehen, aber Sie sollten sich nur Gedanken über die Effizienz machen, wenn Sie dies tatsächlich beobachten. Ich vermute, die meisten wiederholten Ausdrücke, die Sie schreiben, sind wirklich in nur einer Operation im Plan zusammengefasst.

Trotzdem wiederhole ich einen Teil meiner Antwort auf diese Frage:

/dba/19762/why-is-the-select-clause-listed-first


Hier ist Joe Celkos Erklärung, wie eine Abfrage gemäß dem Standard verarbeitet wird (ich habe dies aus meinem eigenen aspfaq.com-Artikel gestohlen, der das Zitat wahrscheinlich aus einem Newsgroup-Beitrag von Celko gestohlen hat):

So funktioniert ein SELECT in SQL ... zumindest theoretisch. Echte Produkte optimieren Dinge, wenn sie können.

Beginnen Sie mit der FROM-Klausel und erstellen Sie eine Arbeitstabelle aus allen Verknüpfungen, Vereinigungen, Schnittpunkten und allen anderen Tabellenkonstruktoren. Mit der Option AS können Sie dieser Arbeitstabelle einen Namen geben, den Sie dann für den Rest der enthaltenen Abfrage verwenden müssen.

Gehen Sie zur WHERE-Klausel und entfernen Sie Zeilen, die keine Kriterien erfüllen. das heißt, das nicht auf TRUE testen (UNKNOWN und FALSE verwerfen). Die WHERE-Klausel wird auf das Arbeiten in der FROM-Klausel angewendet.

Wechseln Sie zur optionalen GROUP BY-Klausel, erstellen Sie Gruppen, und reduzieren Sie jede Gruppe auf eine einzelne Zeile. Ersetzen Sie dabei die ursprüngliche Arbeitstabelle durch die neue gruppierte Tabelle. Die Zeilen einer gruppierten Tabelle müssen Gruppenmerkmale sein: (1) eine Gruppierungsspalte (2) eine Statistik über die Gruppe (dh Aggregatfunktionen) (3) eine Funktion oder (4) ein Ausdruck, der sich aus diesen drei Elementen zusammensetzt.

Wechseln Sie zur optionalen HAVING-Klausel und wenden Sie sie auf die gruppierte Arbeitstabelle an. Wenn es keine GROUP BY-Klausel gab, behandeln Sie die gesamte Tabelle als eine Gruppe.

Gehen Sie zur SELECT-Klausel und konstruieren Sie die Ausdrücke in der Liste. Dies bedeutet, dass die skalaren Unterabfragen, Funktionsaufrufe und Ausdrücke in SELECT ausgeführt werden, nachdem alle anderen Klauseln ausgeführt wurden. Der AS-Operator kann auch Ausdrücken in der SELECT-Liste einen Namen geben. Diese neuen Namen entstehen alle auf einmal, aber nachdem die WHERE-Klausel ausgeführt wurde; Sie können sie daher nicht in der SELECT-Liste oder in der WHERE-Klasse verwenden.

Verschachtelte Abfrageausdrücke folgen den üblichen Gültigkeitsregeln, die Sie von einer blockstrukturierten Sprache wie C, Pascal, Algol usw. erwarten würden. Die innersten Abfragen können nämlich auf Spalten und Tabellen in den Abfragen verweisen, in denen sie enthalten sind.

Dies bedeutet, dass ein SELECT nicht mehr Spalten als ein GROUP BY enthalten kann. aber es kann sicherlich weniger Spalten haben.

Jetzt war Celko einer der Hauptverantwortlichen für die früheren Versionen der Standards. Ich weiß nicht, ob Sie jemals eine endgültige Antwort auf die WHY?Frage bekommen werden, außer für Spekulationen. Ich vermute, dass es dem Parser sehr leicht gemacht wird, die tatsächliche Operation zuerst aufzulisten, um genau zu wissen, um welche Art von Operation es sich handelt. Stellen Sie sich einen Join mit 20 Tabellen vor, der am Ende ein SELECToder UPDATEoder sein könnte DELETE, und denken Sie daran, dass der Code für diese Engines ursprünglich in den Tagen geschrieben wurde, als das Parsen von Zeichenfolgen ziemlich kostspielig war.

Beachten Sie, dass, wenn der SQL-Standard vorschreibt FROM, dass die Hersteller unabhängig voneinander entschieden haben, die Grammatik in einer anderen Reihenfolge zu analysieren. Daher ist es möglicherweise nicht sinnvoll zu erwarten, dass die Reihenfolge der geschriebenen Klauseln der Reihenfolge der Verarbeitung zu 100% entspricht die Zeit.

Gleiches gilt für Dinge wie CASE. Wir haben zum Beispiel hier auf dieser Site Szenarien gesehen , in denen der zuvor angenommene Mythos, der CASEimmer in der richtigen Reihenfolge abläuft, und Kurzschlüsse falsch sind. Dies gilt auch für andere gängige Überzeugungen, z. B. das Auswerten von Joins in der Reihenfolge, in der sie geschrieben wurden, das Kurzschließen von WHEREKlauseln von links nach rechts oder das einmalige oder wiederholte Verarbeiten von CTEs in einer bestimmten Reihenfolge, selbst wenn auf sie mehrfach verwiesen wird. Es steht den Produkten frei, zu optimieren, wie sie es für richtig halten, auch wenn dies nicht genau der Aussage entspricht, dass die Abfrage deklarativ funktionieren sollte.

Aaron Bertrand
quelle
2
Beachten Sie auch, dass die Möglichkeit, Aliase in verschiedenen Teilen der Abfrage zu verwenden oder nicht zu verwenden, vom Parser und nicht vom Optimierer oder der Ausführungsengine erzwungen wird. Wie die Engine die Abfrage tatsächlich ausführt, spiegelt nicht unbedingt die Einschränkungen wider, die sich auf die Syntax auswirken.
Aaron Bertrand
2

In Entity SQL können Sie in bestimmten Situationen Aliase aus Ausdrücken an anderen Stellen in der Abfrage verwenden:

select k1, count(t.a), sum(t.a)
from T as t
group by t.b + t.c as k1

Beachten Sie, dass Sie hier den Ausdruck in der GROUP BYKlausel definieren MÜSSEN, um ihn in der SELECTKlausel zu verwenden.

Es ist offensichtlich möglich , einige dieser Arten von Aliasen als wiederverwendbare Ausdrücke in SQL-Abfragen zuzulassen.

ErikE
quelle