Kapitel 4. Arbeiten mit Sets
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Obwohl du mit deinen Daten Zeile für Zeile arbeiten kannst, geht es bei relationalen Datenbanken eigentlich nur um Mengen. In diesem Kapitel geht es um die Verwendung von Mengenoperatoren, mit denen du Daten aus mehreren Ergebnismengen kombinieren kannst. Ich beginne mit einem kurzen Überblick über die Mengenlehre und zeige dann, wie die Mengenoperatoren union
, intersect
und except
verwendet werden können, um mehrere Datensätze miteinander zu kombinieren.
Mengenlehre Fibel
Vielleicht erinnerst du dich aus dem Matheunterricht an Diagramme wie das in Abbildung 4-1 gezeigte.
Der schattierte Bereich in Abbildung 4-1 stellt die Vereinigung der Mengen A und B dar, wobei der überlappende Bereich nur einmal enthalten ist. Hier ist eine weitere Möglichkeit, die Operation union
zu veranschaulichen, indem du zwei Mengen von ganzen Zahlen verwendest:
A = {1, 2, 4, 7, 9} B = {3, 5, 7, 9} A union B = {1, 2, 3, 4, 5, 7, 9}
In diesem Beispiel enthält Menge A 5 ganze Zahlen und Menge B enthält 4 ganze Zahlen. Jede Menge hat ein paar einzigartige Werte, aber beide Mengen haben die Werte 7 und 9 gemeinsam. Die union
von A und B ergibt insgesamt 7 Werte, wobei die ganzen Zahlen 7 und 9 nur einmal enthalten sind.
Als Nächstes schauen wir uns den Bereich an, den sich zwei Mengen teilen, den sogenannten Schnittpunkt. Abbildung 4-2 zeigt die grafische Darstellung eines Schnittpunkts.
Wenn du dieselben Zahlenmengen wie im letzten Beispiel verwendest, kannst du sehen, dass die Schnittmenge von A und B nur die Zahlen 7 und 9 enthält:
A = {1, 2, 4, 7, 9} B = {3, 5, 7, 9} A intersect B = {7, 9}
Wenn es keine Überschneidung zwischen den beiden Mengen gibt, wäre die Schnittmenge leer, und intersect
ist der einzige Mengenoperator, der eine leere Menge ergeben kann.
Das dritte und letzte Diagramm in Abbildung 4-3 veranschaulicht eine Operation, bei der der Inhalt einer Menge ohne Überschneidungen mit einer anderen Menge zurückgegeben wird.
Diese Operation, die auch als except
bekannt ist (du kannst aber auch minus
verwenden), liefert die gesamte Menge A ohne Überschneidungen mit Menge B. Hier ist die gleiche Operation mit den numerischen Mengen:
A = {1, 2, 4, 7, 9} B = {3, 5, 7, 9} A except B = {1, 2, 4}
Von den drei Mengenoperationen ist except
die einzige, die ein anderes Ergebnis liefert, wenn du die Reihenfolge der Mengen vertauschst. Hier ist das gleiche Beispiel, aber mit dem Ergebnis von B except
A:
A = {1, 2, 4, 7, 9} B = {3, 5, 7, 9} B except A = {3, 5}
Diese drei Set-Operatoren decken viele Situationen ab, aber wie würdest du den in Abbildung 4-4 gezeigten Fall beschreiben?
Die in Abbildung 4-4 gezeigte Operation kann nicht mit einem der drei zuvor beschriebenen Set-Operatoren erreicht werden. Sie kann jedoch durch die Kombination mehrerer Mengenoperatoren erreicht werden. Hier sind zwei verschiedene Möglichkeiten, um den schattierten Bereich in Abbildung 4-4 zu erzeugen:
(A union B) except (A intersect B) -- or -- (A except B) union (B except A)
In den nächsten Abschnitten dieses Kapitels erfährst du, wie du diese Set-Operatoren einsetzen kannst.
Die Gewerkschaft Operator
Mit dem Operator union
kannst du zwei Datensätze kombinieren. Hier ist ein einfaches Beispiel:
PUBLIC>select 1 as numeric_col, 'ABC' as string_col union select 2 as numeric_col, 'XYZ' as string_col; +-------------+------------+ | NUMERIC_COL | STRING_COL | |-------------+------------| | 1 | ABC | | 2 | XYZ | +-------------+------------+
Einfacher geht's nicht: Zwei einzeilige Ergebnismengen ohne überlappende Werte werden zu einer zweizeiligen Ergebnismenge kombiniert. Diese Art von Anweisung wird als Verbundabfrage bezeichnet, weil sie zwei unabhängige Abfragen enthält, die durch einen Mengenoperator getrennt sind. Bevor wir uns fortgeschritteneren Beispielen zuwenden, hier einige Einschränkungen in Bezug auf die beiden zu kombinierenden Mengen:
- Beide Sets müssen die gleiche Anzahl von Spalten enthalten.
- Die Datentypen der Spalten müssen übereinstimmen.
Hier siehst du, wie Snowflake reagiert, wenn die beiden Sets eine unterschiedliche Spaltenanzahl haben:
PUBLIC>select 1 as numeric_col, 'ABC' as string_col union select 2 as numeric_col, 'XYZ' as string_col, 99 as extra_col; 001789 (42601): SQL compilation error: invalid number of result columns for set operator ...
In diesem Fall siehst du eine sehr spezifische Fehlermeldung von Snowflake, aber hier ist der Fehler, der ausgegeben wird, wenn die Spaltentypen nicht übereinstimmen:
PUBLIC>select 1 as numeric_col, 'ABC' as string_col union select 'XYZ' as numeric_col, 2 as string_col; 100038 (22018): Numeric value 'ABC' is not recognized
Diese Fehlermeldung ist allgemeiner gehalten, aber sie besagt, dass der Wert 'ABC'
mit dem in der ersten Spalte der ersten Abfrage verwendeten Datentyp (die Zahl 1) nicht übereinstimmt.
Wie bereits erwähnt, entfernt der union
Operator beim Aufbau der Ergebnismenge Duplikate, so dass Überschneidungen nur einmal dargestellt werden. Hier sind die beiden zuvor verwendeten Mengen ({1, 2, 4, 7, 9} und {3, 5, 7, 9}), die mit der Subklausel values
erstellt und dann mit union
kombiniert wurden:
PUBLIC>select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val) union select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val); +-------------+ | INTEGER_VAL | |-------------| | 1 | | 2 | | 4 | | 7 | | 9 | | 3 | | 5 | +-------------+
Wie du siehst, enthalten beide Mengen 7 und 9, aber diese Werte erscheinen nur einmal in der Ergebnismenge, weil der union
Operator die Werte sortiert und Duplikate entfernt.
Dies ist zwar das Standardverhalten, aber es gibt Fälle, in denen du nicht möchtest, dass die Duplikate entfernt werden. In diesem Fall kannst du union all
verwenden:
PUBLIC>select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val) union all select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val); +-------------+ | INTEGER_VAL | |-------------| | 1 | | 2 | | 4 | | 7 | | 9 | | 3 | | 5 | | 7 | | 9 | +-------------+
Die Ergebnismenge umfasst nun neun statt sieben Zeilen, wobei die doppelten 7er und 9er die zusätzlichen zwei Zeilen bilden.
Die Beispieldatenbank enthält Daten aus den Jahren 1992 bis 1998; hier ist eine Abfrage, die union
verwendet, um alle Kunden zu finden, die 1992 oder 1993 Bestellungen über 350.000 $ aufgegeben haben:
PUBLIC>select distinct o_custkey from orders where o_totalprice > 350000 and date_part(year, o_orderdate) = 1992 union select distinct o_custkey from orders where o_totalprice > 350000 and date_part(year, o_orderdate) = 1993; +-----------+ | O_CUSTKEY | |-----------| | 114856 | | 116683 | | 4334 | | 61790 | | 24275 | | 20665 | | 130745 | | 70054 | | 122047 | | 123934 | ... <847 rows omitted> | 102304 | | 106813 | | 109519 | | 120437 | | 131131 | | 130987 | | 108745 | | 5330 | | 95137 | | 59665 | +-----------+
Diese Abfrage hätte auch mit einer einzigen Filterbedingung und ohne den union
Operator geschrieben werden können, aber diese Form der Abfrage wird nützlich sein, um die Ergebnisse von intersect
und except
in den nächsten beiden Abschnitten zu vergleichen.
Der intersect Operator
Wie weiter oben im Kapitel beschrieben (siehe "Mengenlehre-Fibel"), wird der intersect
Operator verwendet, um die Schnittmenge zwischen zwei Datensätzen zu finden. Hier ist ein Beispiel mit den beiden numerischen Datensätzen von vorhin:
PUBLIC>select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val) intersect select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val); +-------------+ | INTEGER_VAL | |-------------| | 7 | | 9 | +-------------+
Die Überschneidung der Mengen A und B sind die Werte 7 und 9; alle anderen Werte sind für jede Menge einzigartig.
Im vorherigen Abschnitt hat die Abfrage der Tabelle Orders
mit union
867 Zeilen ergeben. Schauen wir uns an, was passiert, wenn union
für dieselbe Abfrage in intersect
geändert wird:
PUBLIC>select distinct o_custkey from orders where o_totalprice > 350000 and date_part(year, o_orderdate) = 1992 intersect select distinct o_custkey from orders where o_totalprice > 350000 and date_part(year, o_orderdate) = 1993; +-----------+ | O_CUSTKEY | |-----------| | 100510 | +-----------+
Es gibt zwar 867 verschiedene Kunden, die 1992 oder 1993 Aufträge über 350.000 $ erteilt haben, aber nur einer von ihnen hat in beiden Jahren solche Aufträge erteilt.
Die Ausnahme Operator
Der dritte und letzte der Mengenoperatoren ist except
, der verwendet wird, um Zeilen aus Menge A ohne Überschneidungen mit Menge B zurückzugeben. Hier ist ein Beispiel mit den beiden numerischen Datensätzen von vorhin:
PUBLIC>select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val) except select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val);
+-------------+ | INTEGER_VAL | |-------------| | 1 | | 2 | | 4 | +-------------+
Diese Operation ergibt die Menge {1, 2, 4}, also die drei Werte, die in Menge A gefunden wurden, aber nicht in Menge B. Wenn du die Reihenfolge der beiden Mengen vertauschst, erhältst du ein anderes Ergebnis:
PUBLIC>select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val) except select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val); +-------------+ | INTEGER_VAL | |-------------| | 3 | | 5 | +-------------+
Im Gegensatz zu union
und intersect
, die unabhängig davon, welche Menge oberhalb oder unterhalb des Mengenoperators liegt, die gleichen Ergebnisse liefern, musst du beachten, dass A except
B normalerweise andere Ergebnisse liefert als B except
A.
Zurück zur Abfrage von Orders
: Hier sind die Ergebnisse, wenn du except
verwendest:
PUBLIC>select distinct o_custkey from orders where o_totalprice > 350000 and date_part(year, o_orderdate) = 1992 except select distinct o_custkey from orders where o_totalprice > 350000 and date_part(year, o_orderdate) = 1993; +-----------+ | O_CUSTKEY | |-----------| | 134878 | | 125183 | | 44240 | | 79138 | | 71119 | ... <444 rows omitted> | 74929 | | 149240 | | 23587 | | 79999 | | 46490 | +-----------+
Diese Abfrage liefert 454 Kunden und wenn die Reihenfolge der Sets umgekehrt wird (Reihenfolge ab 1993 zuerst), liefert die Abfrage 412 Zeilen.
Betriebsregeln festlegen
In den folgenden Abschnitten findest du einige Regeln, die du bei der Arbeit mit Mengenoperatoren beachten solltest.
Sortieren von zusammengesetzten Abfrageergebnissen
Wenn du möchtest, dass deine Ergebnisse sortiert werden, wenn du Set-Operatoren verwendest, musst du die folgenden Regeln befolgen:
- Es darf nur eine
order by
Klausel geben, und die muss am Ende der Anweisung stehen. - Wenn du Spaltennamen in der
order by
Klausel angibst, musst du die Spaltennamen/Aliase aus der ersten Abfrage verwenden.
Was den zweiten Punkt betrifft, so können die einzelnen Abfragen, die als Teil einer zusammengesetzten Anweisung verwendet werden, Daten aus verschiedenen Tabellen mit unterschiedlichen Spaltennamen abrufen. Daher bestimmen die in der ersten Abfrage verwendeten Spaltennamen, was in der order by
Klausel angegeben werden kann. Hier ist ein Beispiel:
PUBLIC>select distinct o_orderdate from orders intersect select distinct l_shipdate from lineitem order by o_orderdate; +-------------+ | O_ORDERDATE | |-------------| | 1992-01-03 | | 1992-01-04 | | 1992-01-05 | | 1992-01-06 | | 1992-01-07 | ... <2,394 rows omitted> | 1998-07-29 | | 1998-07-30 | | 1998-07-31 | | 1998-08-01 | | 1998-08-02 | +-------------+
In diesem Beispiel muss die Spalte, nach der sortiert werden soll, nach o_orderdate
und nicht nach l_shipdate
sortiert werden. Das passiert, wenn du l_shipdate
als Sortierspalte angibst:
PUBLIC>select distinct o_orderdate from orders intersect select distinct l_shipdate from lineitem order by l_shipdate; 000904 (42000): SQL compilation error: error line 4 at position 9 invalid identifier 'L_SHIPDATE'
Um in diesen Fällen Verwirrung zu vermeiden, kannst du für beide Abfragen und übereinstimmende Spaltenaliase angeben und den Alias in deiner order by
Klausel verwenden.
Operation Precedence einstellen
Wenn deine zusammengesetzte Abfrage mehr als zwei Abfragen enthält, die unterschiedliche Mengenoperatoren verwenden, musst du dir überlegen, in welcher Reihenfolge du die Abfragen platzierst, um die gewünschten Ergebnisse zu erzielen. Abbildung 4-5 zeigt die "Mystery Operation", die bereits in Abbildung 4-4 vorgestellt wurde.
Schauen wir uns an, wie das mit den beiden einfachen numerischen Sets, die in diesem Kapitel verwendet werden, erreicht werden kann:
PUBLIC>select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val); +-------------+ | INTEGER_VAL | |-------------| | 1 | | 2 | | 4 | | 7 | | 9 | +-------------+ PUBLIC>select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val); +-------------+ | INTEGER_VAL | |-------------| | 3 | | 5 | | 7 | | 9 | +-------------+
Wie bereits erwähnt, gibt es mehrere Möglichkeiten, den schattierten Bereich in Abbildung 4-5 zu erzeugen, der in diesem Fall die Menge {1, 2, 3, 4, 5} ist, aber wir wählen die zweite:
(A except B) union (B except A)
So könnte das für die beiden numerischen Sets aussehen:
PUBLIC>select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val) except select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val) union select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val) except select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val); +-------------+ | INTEGER_VAL | |-------------| | 3 | | 5 | +-------------+
Alle Teile scheinen an ihrem Platz und in der richtigen Reihenfolge zu sein, aber die Ergebnisse scheinen nicht korrekt zu sein. Das Problem liegt darin, dass der Server diese Vorgänge von oben nach unten ausführt, obwohl sie zusammengefügt werden müssen. Die beiden Operationen except
müssen unabhängig voneinander durchgeführt werden, und die beiden resultierenden Mengen können dann mit union
zu zusammengefügt werden. Dazu musst du Klammern verwenden:
PUBLIC>(select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val) except select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val) ) union (select integer_val from (values (3), (5), (7), (9)) as set_b (integer_val) except select integer_val from (values (1), (2), (4), (7), (9)) as set_a (integer_val) ); +-------------+ | INTEGER_VAL | |-------------| | 1 | | 2 | | 4 | | 5 | | 3 | +-------------+
Wenn du zusammengesetzte Abfragen mit drei oder mehr Set-Operatoren erstellst, solltest du überlegen, wie die verschiedenen Operationen gruppiert werden müssen, um die gewünschten Ergebnisse zu erzielen.
Nachbereitung
In diesem Kapitel hast du gelernt, wie die Mengenoperatoren union
, intersect
und except
verwendet werden können, um Überschneidungen zwischen mehreren Datensätzen zu erkennen. Außerdem hast du Strategien kennengelernt, wie du diese Operatoren kombinieren kannst, um komplexe Beziehungen zwischen Datensätzen zu erkennen. Auch wenn du die Mengenoperatoren in späteren Kapiteln nicht mehr in vielen Beispielen sehen wirst, solltest du diese leistungsstarken Werkzeuge im Hinterkopf behalten, wenn du Datenanalysen durchführst oder Berichte erstellst.
Teste dein Wissen
Die folgenden Übungen dienen dazu, dein Verständnis der Mengenoperatoren zu testen. Die Lösungen findest du in "Kapitel 4" im Anhang B.
Übung 4-1
Gegeben sind die folgenden zwei Sets:
A = {3, 5, 7, 9} B = {4, 5, 6, 7, 8}
Welche Mengen werden bei jeder der folgenden Operationen erzeugt?
- A Gewerkschaft B
- A Vereinigung alle B
- A schneidet B
- A außer B
- B außer A
Übung 4-2
Schreibe eine zusammengesetzte Abfrage, die die Namen aller Regionen (Region.r_name
) und Nationen (Nation.n_name
) zurückgibt, die mit dem Buchstaben A
beginnen.
Übung 4-3
Ändere die Abfrage aus Übung 4-2 so ab, dass sie nach Namen sortiert (die Standardsortierung ist in Ordnung).
Get Snowflake SQL und Skripting lernen now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.