Untersuchen der SELECT-Anweisung

Abgeschlossen

Transact-SQL oder T-SQL ist ein Dialekt der SQL-Sprache (nach ANSI-Standard), die von Microsoft SQL-Produkten und -Diensten verwendet wird. Er ist ähnlich wie Standard-SQL. Unser Hauptschwerpunkt wird auf der SELECT-Anweisung liegen, bei der es mit Abstand die meisten Optionen und Varianten einer DML-Anweisung gibt.

Sehen wir uns zuerst an, wie eine SELECT-Anweisung verarbeitet wird. Die Reihenfolge, in der eine SELECT-Anweisung geschrieben wird, ist nicht die Reihenfolge, in der sie von der SQL Server-Datenbank-Engine ausgewertet und verarbeitet wird.

Betrachten Sie die folgende Abfrage:

SELECT OrderDate, COUNT(OrderID) AS Orders
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
ORDER BY OrderDate DESC;

Die Abfrage besteht aus einer SELECT-Anweisung, die wiederum aus mehreren Klauseln besteht. Jede Klausel definiert einen bestimmten Vorgang, der auf die abgerufenen Daten angewendet werden muss. Bevor wir die Laufzeitreihenfolge von Vorgängen untersuchen, sehen wir uns kurz an, welche Aufgabe diese Abfrage hat, obwohl die Details der verschiedenen Klauseln in diesem Modul nicht behandelt werden.

Die SELECT-Klausel gibt die Spalte OrderDate und die Anzahl der OrderID-Werte zurück, denen der Name (oder der Alias) Orders zugewiesen wird:

SELECT OrderDate, COUNT(OrderID) AS Orders

Die FROM-Klausel identifiziert, welche Tabelle die Quelle der Zeilen für die Abfrage ist. In diesem Fall ist das die Tabelle Sales.SalesOrder:

FROM Sales.SalesOrder

Die WHERE-Klausel filtert Zeilen aus den Ergebnissen, wobei nur diejenigen Zeilen, die die angegebene Bedingung erfüllen, beibehalten werden. In diesem Fall sind das Aufträge mit dem Status „shipped“ (versendet):

WHERE Status = 'Shipped'

Die GROUP BY-Klausel nimmt die Zeilen, die die Filterbedingung erfüllt haben, und gruppiert sie nachOrderDate, sodass alle Zeilen mit demselben OrderDate als eine einzelne Gruppe betrachtet werden und für jede Gruppe eine einzige Zeile zurückgegeben wird:

GROUP BY OrderDate

Nachdem die Gruppen gebildet wurden, filtert die HAVING-Klausel die Gruppen basierend auf ihrem eigenen Prädikat. In die Ergebnisse werden nur Datumsangaben mit mehr als einer Bestellung einbezogen:

HAVING COUNT(OrderID) > 1

Für eine Vorschau dieser Abfrage ist die letzte Klausel „ORDER BY“. Sie sortiert die Ausgabe in absteigender Reihenfolge von OrderDate:

ORDER BY OrderDate DESC;

Nachdem Sie gesehen haben, welche Aufgabe die einzelnen Klauseln haben, sehen wir uns jetzt die Reihenfolge an, in der SQL Server sie tatsächlich auswertet:

  1. Die FROM-Klausel wird zuerst ausgewertet, um die Quellzeilen für den restlichen Teil der Anweisung bereitzustellen. Eine virtuelle Tabelle wird erstellt und an den nächsten Schritt übergeben.
  2. Anschließend wird die WHERE-Klausel ausgewertet. Sie filtert diejenigen Zeilen aus der Quelltabelle, die mit einem Prädikat übereinstimmen. Die gefilterte virtuelle Tabelle wird an den nächsten Schritt übergeben.
  3. GROUP BY ist die nächste Klausel. Sie organisiert die Zeilen in der virtuellen Tabelle entsprechend eindeutigen Werten, die in der „GROUP BY“-Liste enthalten sind. Eine neue virtuelle Tabelle mit der Gruppenliste wird erstellt und an den nächsten Schritt übergeben. Ab diesem Punkt im Arbeitsablauf wird von anderen Elementen möglicherweise nur auf Spalten in der „GROUP BY“-Liste oder auf Aggregatfunktionen verwiesen.
  4. Als Nächstes wird die HAVING-Klausel ausgewertet. Sie filtert ganze Gruppen basierend auf ihrem Prädikat heraus. Die in Schritt 3 erstellte virtuelle Tabelle wird gefiltert und an den nächsten Schritt übergeben.
  5. Schließlich wird die SELECT-Klausel ausgeführt. Sie bestimmt, welche Spalten in den Abfrageergebnissen angezeigt werden sollen. Weil die SELECT-Klausel nach den anderen Schritten ausgewertet wird, können alle dort erstellten Spaltenaliase (in unserem Beispiel, Orders) in der GROUP BY- oder HAVING-Klausel nicht verwendet werden.
  6. Die ORDER BY-Klausel ist die letzte auszuführende Klausel. Sie sortiert die Zeilen so, wie es durch die zugehörige Spaltenliste festgelegt wurde.

Wenn Sie dieses Wissen auf unsere Beispielabfrage anwenden möchten – hier ist die logische Reihenfolge zur Laufzeit der vorstehenden SELECT-Anweisung:

FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate 
HAVING COUNT(OrderID) > 1
SELECT OrderDate, COUNT(OrderID) AS Orders
ORDER BY OrderDate DESC;

Nicht alle diese möglichen Klauseln sind in jeder von Ihnen geschriebenen SELECT-Anweisung erforderlich. Die einzige erforderliche Klausel ist die SELECT-Klausel, die in einigen Fällen eigenständig verwendet werden kann. Normalerweise wird auch eine FROM-Klausel einbezogen, um die gerade abgefragte Tabelle zu identifizieren. Darüber hinaus gibt es in Transact-SQL andere Klauseln, die hinzugefügt werden können.

Wie Sie gesehen haben, schreiben Sie T-SQL-Abfragen nicht in derselben Reihenfolge, in der sie logisch ausgewertet werden. Die Laufzeitreihenfolge der Auswertung bestimmt, welche Daten für welche Klauseln zur Verfügung stehen. Eine Klausel kann nämlich nur auf die Informationen zugreifen, die über eine bereits verarbeitete Klausel bereits verfügbar gemacht wurden. Aus diesem Grund ist es wichtig, beim Schreiben von Abfragen die echte logische Verarbeitungsreihenfolge zu verstehen.

Auswählen aller Spalten

Die SELECT-Klausel wird oft als SELECT-Liste bezeichnet, weil sie die Werte auflistet, die in den Ergebnissen der Abfrage zurückgegeben werden sollen.

Die einfachste Form einer SELECT-Klausel ist die Verwendung des Sternchens (*) zum Zurückgeben aller Spalten. Bei Verwendung in T-SQL-Abfragen wird dieses Zeichen als Stern bezeichnet. Obwohl SELECT * für einen Schnelltest geeignet ist, sollten Sie diese Option in der Produktionsarbeit aus den folgenden Gründen nicht verwenden:

  • Änderungen an der Tabelle, die Spalten hinzufügen oder neu anordnen, werden in den Abfrageergebnissen widergespiegelt. Das kann zu einer unerwarteten Ausgabe bei Anwendungen oder Berichten führen, die diese Abfrage verwenden.
  • Die Rückgabe von nicht benötigten Daten kann Ihre Abfragen verlangsamen und zu Leistungsproblemen führen, wenn die Quelltabelle eine große Anzahl von Zeilen enthält.

Im folgenden Beispiel werden beispielsweise alle Spalten aus der (hypothetischen) Tabelle Production.Product abgerufen.

SELECT * FROM Production.Product;

Das Ergebnis dieser Abfrage ist ein Rowset, das alle Spalten für alle Zeilen der Tabelle enthält, die ungefähr so aussehen könnten:

ProductID

Name

ProductNum

Color

StandardCost

ListPrice

Size

Weight

ProductCatID

680

HL Road Frame - Black, 58

FR-R92B-58

Schwarz

1.059,31

1.431,5

58

1016.04

18

706

HL Road Frame - Red, 58

FR-R92R-58

Red

1.059,31

1.431,5

58

1016.04

18

707

Sport-100 Helmet, Red

HL-U509-R

Red

13.0863

34.99

35

708

Sport-100 Helmet, Black

HL-U509

Schwarz

13.0863

34.99

35

...

...

...

...

...

...

...

...

...

Auswählen bestimmter Spalten

Mithilfe einer expliziten Spaltenliste können Sie genau steuern, welche Spalten in welcher Reihenfolge zurückgegeben werden. Jede Spalte im Ergebnis hat den Namen der Spalte als Kopfzeile.

Sehen Sie sich beispielsweise die folgende Abfrage an, die wieder die hypothetische Tabelle Production.Product verwendet.

SELECT ProductID, Name, ListPrice, StandardCost
‎FROM Production.Product;

Dieses Mal enthalten die Ergebnisse nur die angegebenen Spalten:

ProductID

Name

ListPrice

StandardCost

680

HL Road Frame - Black, 58

1.431,5

1.059,31

706

HL Road Frame - Red, 58

1.431,5

1.059,31

707

Sport-100 Helmet, Red

34.99

13.0863

708

Sport-100 Helmet, Black

34.99

13.0863

...

...

...

...

Auswählen von Ausdrücken

Zusätzlich zum Abrufen von Spalten, die in der angegebenen Tabelle gespeichert sind, kann eine SELECT-Klausel Berechnungen und Bearbeitungen durchführen, bei denen Operatoren zum Kombinieren von Spalten und Werten oder von mehreren Spalten verwendet werden. Das Ergebnis der Berechnung oder Bearbeitung muss ein einwertiges (skalares) Ergebnis sein, das im Ergebnis als separate Spalte angezeigt wird.

Die folgende Abfrage beispielsweise enthält zwei Ausdrücke:

SELECT ProductID,
      Name + '(' + ProductNumber + ')',
  ListPrice - StandardCost
FROM Production.Product;

Die Ergebnisse dieser Abfrage könnten ungefähr so aussehen:

ProductID

680

HL Road Frame – Black, 58(FR-R92B-58)

372,19

706

HL Road Frame – Red, 58(FR-R92R-58)

372,19

707

Sport-100 Helmet, Red(HL-U509-R)

21.9037

708

Sport-100 Helmet, Black(HL-U509)

21.9037

...

...

...

Bei diesen Ergebnissen müssen einige interessante Aspekte beachtet werden:

  • Die von den beiden Ausdrücken zurückgegebenen Spalten haben keine Spaltennamen. Je nachdem, mit welchem Tool Sie Ihre Abfrage übermitteln, könnte ein fehlender Spaltenname durch eine leere Spaltenüberschrift, einen literalen Indikator „kein Spaltenname“ oder einen Standardnamen wie column1 angegeben werden. Später in diesem Abschnitt werden Sie sehen, wie Sie in der Abfrage einen Alias für den Spaltennamen angeben.
  • Im ersten Ausdruck wird der Operator + verwendet, um (zeichenbasierte) Zeichenfolgenwerte zu verketten, während im zweiten Ausdruck der Operator - verwendet wird, um einen numerischen Wert von einem anderen zu subtrahieren. Wenn der Operator + bei numerischen Werten verwendet wird, führt er eine Addition aus. Natürlich ist es wichtig, die Datentypen der Spalten zu verstehen, die Sie in Ausdrücke einbeziehen. Datentypen werden im nächsten Abschnitt erläutert.

Angeben von Spaltenaliasen

Sie können für jede von der SELECT-Abfrage zurückgegebene Spalte einen Alias angeben – entweder als Alternative zum Namen der Quellspalte oder zum Zuweisen eines Namens zur Ausgabe eines Ausdrucks.

Hier ist beispielsweise die gleiche Abfrage wie zuvor, aber mit Aliasen, die für die einzelnen Spalten angegeben wurden:

SELECT ProductID AS ID,
      Name + '(' + ProductNumber + ')' AS ProductName,
  ListPrice - StandardCost AS Markup
FROM Production.Product;

Die Ergebnisse aus dieser Abfrage enthalten die angegebenen Spaltennamen:

ID

ProductName

Markup

680

HL Road Frame – Black, 58(FR-R92B-58)

372,19

706

HL Road Frame – Red, 58(FR-R92R-58)

372,19

707

Sport-100 Helmet, Red(HL-U509-R)

21.9037

708

Sport-100 Helmet, Black(HL-U509)

21.9037

...

...

...

Hinweis

Das Schlüsselwort „AS“ ist optional, wenn ein Alias angegeben wird. Es hat sich aber bewährt, dieses Schlüsselwort zur Verdeutlichung mit einzubeziehen.

Formatieren von Abfragen

Möglicherweise stellen Sie anhand der Beispiele in diesem Abschnitt fest, dass Sie beim Formatieren Ihres Abfragecodes flexibel sein können. So können Sie beispielsweise jede Klausel (oder die ganze Abfrage) in eine einzige Zeile schreiben oder sie über mehrere Zeilen aufteilen. In den meisten Datenbanksystemen wird die Groß-/Kleinschreibung nicht beachtet, und einige Elemente der T-SQL-Sprache sind optional (einschließlich des Schlüsselworts „AS“, wie bereits erwähnt, und sogar des Semikolons am Ende einer Anweisung).

Beachten Sie die folgenden Richtlinien, damit Ihr T-SQL-Code einfach lesbar (und deshalb einfacher zu verstehen und zu debuggen!) ist:

  • Schreiben Sie T-SQL-Schlüsselwörter wie „SELECT“, „FROM“, „AS“ usw. in Großbuchstaben. Die Großschreibung von Schlüsselwörtern ist eine häufig verwendete Konvention, dank derer jede Klausel einer komplexen Anweisung einfacher gefunden werden kann.
  • Beginnen Sie für jede Hauptklausel einer Anweisung eine neue Zeile.
  • Wenn die „SELECT“-Liste mehr als ein paar Spalten, Ausdrücke oder Aliase enthält, sollten Sie jede Spalte in einer eigenen Zeile auflisten.
  • Ziehen Sie Zeilen mit Unterklauseln oder Spalten ein, um zu verdeutlichen, welcher Code zu der jeweiligen Hauptklausel gehört.