Vereinigungsmengen in SQL (MySQL )
Die UNION-Vereinigung
Eine UNION-Vereinigung mehrerer Tabellen ist wieder eine Tabelle.
Die zu vereinigenden Tabellen müssen alle gleich viele Spalten besitzen; die Ergebnistabelle besitzt dann ebensoviele Spalten. Beispielsweise ergibt die Vereinigung von fünf Tabellen mit jeweils drei Spalten eine neue Tabelle, die ebenfalls drei Spalten umfaßt.
Eine Zeile ist genau dann in der UNION-Vereinigung enthalten, wenn sie in einer der Ausgangstabellen enthalten ist.
Das folgende Beispiel zeigt die Vereinigung dreier Tabellen.
main.sql
WARNINGS; SET sql_mode = 'ANSI,TRADITIONAL';
DROP SCHEMA S; CREATE SCHEMA S; USE S;CREATE TABLE A ( N VARCHAR ( 255 ));
INSERT INTO A ( N ) VALUES ( '0' );
INSERT INTO A ( N ) VALUES ( '1' );SELECT N FROM A;
CREATE TABLE B ( M VARCHAR ( 255 ));
INSERT INTO B ( M ) VALUES ( '0' );
INSERT INTO B ( M ) VALUES ( '5' );SELECT M FROM B;
CREATE TABLE C ( O VARCHAR ( 255 ));
INSERT INTO C ( O ) VALUES ( '5' );SELECT O FROM C;
SELECT N FROM A
UNION
SELECT M FROM B
UNION
SELECT O FROM C;- Protokoll
+------+
| N |
+------+
| 0 |
| 1 |
+------+
+------+
| M |
+------+
| 0 |
| 5 |
+------+
+------+
| O |
+------+
| 5 |
+------+
+------+
| N |
+------+
| 0 |
| 1 |
| 5 |
+------+
Wie man sieht, erscheinen mehrfach vorkommende Zeilen nur einmal im Ergebnis.
»UNION« »ALL«
Um die Vereinigung gleicher Zeilen zu verhindern, verwende man »UNION« »ALL« an Stelle von »UNION«.
main.sql
WARNINGS; SET sql_mode = 'ANSI,TRADITIONAL';
DROP SCHEMA S; CREATE SCHEMA S; USE S;CREATE TABLE A ( N VARCHAR ( 255 ));
INSERT INTO A ( N ) VALUES ( '0' );
INSERT INTO A ( N ) VALUES ( '1' );SELECT N FROM A;
CREATE TABLE B ( M VARCHAR ( 255 ));
INSERT INTO B ( M ) VALUES ( '0' );
INSERT INTO B ( M ) VALUES ( '5' );SELECT M FROM B;
CREATE TABLE C ( O VARCHAR ( 255 ));
INSERT INTO C ( O ) VALUES ( '5' );SELECT O FROM C;
SELECT N FROM A
UNION ALL
SELECT M FROM B
UNION ALL
SELECT O FROM C;- Protokoll
+------+
| N |
+------+
| 0 |
| 1 |
+------+
+------+
| M |
+------+
| 0 |
| 5 |
+------+
+------+
| O |
+------+
| 5 |
+------+
+------+
| N |
+------+
| 0 |
| 1 |
| 0 |
| 5 |
| 5 |
+------+
Eine Abfrage mit »UNION« »ALL« ist oft schneller als eine Abfrage mit »UNION« alleine, sie kann daher verwendet werden, wenn man sicher ist, daß die zu vereinigenden Tabellen ohnehin keine Zeilen enthalten, die in mehreren Tabellen vorkommen.
Es wäre kein Zeitgewinn nach einer Abfrage mit »UNION« »ALL« Duplikate zu entfernen, es kann jedoch unter Umständen ein Zeitgewinn sein vor einer Abfrage mit »UNION« »ALL« durch eine WHERE-Klausel Zeilen auszuschließen, die zu Duplikaten führen, wenn dies in einem Einzelfall möglich sein sollte. Ob dies dann wirklich zu einem Geschwindigkeitsgewinn führt, muß dann aber gemessen werden!
Die Zuordnung von Spalten ⃗
Das folgende Beispiel zeigt, wie von MySQL Spalten über ihre Position einander zugeordnet werden und nicht über ihren Namen.
main.sql
WARNINGS; SET sql_mode = 'ANSI,TRADITIONAL';
DROP SCHEMA S; CREATE SCHEMA S; USE S;CREATE TABLE A ( N VARCHAR ( 255 ), X VARCHAR ( 255 ));
INSERT INTO A ( N, X ) VALUES ( 'AN', 'AX' );SELECT N, X FROM A;
CREATE TABLE B ( X VARCHAR ( 255 ), N VARCHAR ( 255 ));
INSERT INTO B ( X, N ) VALUES ( 'BY', 'BN' );SELECT X, N FROM B;
SELECT N, X FROM A
UNION
SELECT X, N FROM B;- Protokoll
+------+------+
| N | X |
+------+------+
| AN | AX |
+------+------+
+------+------+
| X | N |
+------+------+
| BY | BN |
+------+------+
+------+------+
| N | X |
+------+------+
| AN | AX |
| BY | BN |
+------+------+
Es ist nicht nötig, daß die Namen der Spalten übereinstimmen. Da es jedoch zu Mißverständnissen führen kann, wenn Tabellen mit Spalten unterschiedlicher Namen vereinigt werden, ist es am besten, nur Tabellen zu vereinigen, bei denen Spalten an derselben Position auch den gleichen Namen haben.
Die Syntax (vereinfacht) ⃗
Die schon bekannten mit »SELECT« beginnenden Abfragen nennen wir nun Abfragespezifikationen.
Wir legen die Definition eines Abfrageausdrucks nun so fest, daß dieser entweder eine Abfragespezifikation ist oder eine UNION-Kombination eines Abfrageausdrucks und einer Abfragespezifikation.
Dies erlaubt beispielsweise die UNION-Kombination zweier SELECT-Abfragen (dann ist der »Abfrageausdruck« vor »UNION« eine Abfragespezifikation) oder auch dreier Abfragen (dann ist der »Abfrageausdruck« vor »UNION« selber wieder eine UNION-Kombination zweier SELECT-Abfragen).
- Syntaxdiagramm
Abfrageausdruck
.----------------------.
---.--->| Abfragespezifikation |---------------------------------------------------------------.--->
| '----------------------' ^
| .----------------------. .-----. .---. .----------------------. |
'--->| Abfrageausdruck |--->( UNION )---.--->( ALL )---.--->| Abfragespezifikation |---'
'----------------------' '-----' | '---' ^ '----------------------'
'--------------'Abfragespezifikation
.------. .--------------. .------------------.
--->( SELECT )--->| Auswahlliste |--->| Tabellenausdruck |--->
'------' '--------------' '------------------'
Anpassungen früher gezeigter Syntaxdiagramme:
- Bisherige Version des Syntaxdiagramms
Direkte Datenanweisung
.----------------------.
--->| Abfragespezifikation |--->
'----------------------'- Neue Version des Syntaxdiagramms
Direkte Datenanweisung
.-----------------.
--->| Abfrageausdruck |--->
'-----------------'- Bisherige Version des Syntaxdiagramms
(11.31)
Ansichtsdefinition.------. .----. .------------. .--. .----------------------.
--->( CREATE )--->( VIEW )--->| Bezeichner |--->( AS )--->| Abfragespezifikation |--->
'------' '----' '------------' '--' '----------------------'
Name der Ansicht zu speichernde Abfragespezifikation- Neue Version des Syntaxdiagramms
(11.31)
Ansichtsdefinition.------. .----. .------------. .--. .-----------------.
--->( CREATE )--->( VIEW )--->| Bezeichner |--->( AS )--->| Abfrageausdruck |--->
'------' '----' '------------' '--' '-----------------'
Name der Ansicht zu speichernder Abfrageausdruck
Beispiel Das symmetrische kartesische Produkt zweier Tabellen
Das symmetrische kartesische Produkt zweier Tabellen ist die Vereinigung des kartesischen Produkts der beiden Tabellen mit dem kartesischen Produkt der beiden Tabellen mit vertauschter Reihenfolge der Faktoren.
Beispielsweise sollen in einem Schachturnier jeder der Spieler einer Mannschaft M gegen jeden der Spieler einer Mannschaft W zweimal antreten: Einmal soll der Spieler aus A mit Weiß spielen und einmal der Spieler aus B.
main.sql
WARNINGS; SET sql_mode = 'ANSI,TRADITIONAL';
DROP SCHEMA S; CREATE SCHEMA S; USE S;CREATE TABLE M ( NAME VARCHAR ( 255 ));
INSERT INTO M ( NAME ) VALUES ( 'Rudolf' );
INSERT INTO M ( NAME ) VALUES ( 'Werner' );
INSERT INTO M ( NAME ) VALUES ( 'Reinhard' );CREATE TABLE W ( NAME VARCHAR ( 255 ));
INSERT INTO W ( NAME ) VALUES ( 'Heike' );
INSERT INTO W ( NAME ) VALUES ( 'Claudia' );
INSERT INTO W ( NAME ) VALUES ( 'Anita' );SELECT M.NAME AS WEISS, W.NAME AS SCHWARZ FROM M INNER JOIN W
UNION
SELECT W.NAME AS WEISS, M.NAME AS SCHWARZ FROM W INNER JOIN M;- Protokoll
+----------+----------+
| WEISS | SCHWARZ |
+----------+----------+
| Rudolf | Heike |
| Werner | Heike |
| Reinhard | Heike |
| Rudolf | Claudia |
| Werner | Claudia |
| Reinhard | Claudia |
| Rudolf | Anita |
| Werner | Anita |
| Reinhard | Anita |
| Heike | Rudolf |
| Claudia | Rudolf |
| Anita | Rudolf |
| Heike | Werner |
| Claudia | Werner |
| Anita | Werner |
| Heike | Reinhard |
| Claudia | Reinhard |
| Anita | Reinhard |
+----------+----------+
Die Vereinigung von Basistabellen als Symptom
Die Vereinigung von Basistabellen könnte ein Symptom für Fehler beim Datenbankentwurf sein. Denn alle Datensätze mit gleicher Struktur sollten in einer einzigen Basistabelle enthalten sein.
Man sollte also nicht zwei Tabellen anlegen und diese bei Bedarf mit einer UNION-Abfrage zu einer Tabelle vereinigen.
main.sql
WARNINGS; SET sql_mode = 'ANSI,TRADITIONAL';
DROP SCHEMA S; CREATE SCHEMA S; USE S;CREATE TABLE KLASSE7A ( NAME VARCHAR ( 255 ), RUFNUMMER VARCHAR ( 255 ));
INSERT INTO KLASSE7A ( NAME, RUFNUMMER ) VALUES ( 'Rosa', '324' );
INSERT INTO KLASSE7A ( NAME, RUFNUMMER ) VALUES ( 'Brigitte', '523' );
INSERT INTO KLASSE7A ( NAME, RUFNUMMER ) VALUES ( 'Uwe', '245' );CREATE TABLE KLASSE7B ( NAME VARCHAR ( 255 ), RUFNUMMER VARCHAR ( 255 ));
INSERT INTO KLASSE7B ( NAME, RUFNUMMER ) VALUES ( 'Matthias', '522' );
INSERT INTO KLASSE7B ( NAME, RUFNUMMER ) VALUES ( 'Gertken', '423' );
INSERT INTO KLASSE7B ( NAME, RUFNUMMER ) VALUES ( 'Elke', '811' );SELECT NAME, RUFNUMMER FROM KLASSE7A
UNION
SELECT NAME, RUFNUMMER FROM KLASSE7B;- Protokoll
+----------+-----------+
| NAME | RUFNUMMER |
+----------+-----------+
| Rosa | 324 |
| Brigitte | 523 |
| Uwe | 245 |
| Matthias | 522 |
| Gertken | 423 |
| Elke | 811 |
+----------+-----------+
Besser ist es eine Tabelle anzulegen und diese bei Bedarf mit einer WHERE-Klausel in Tabellen für einzelne Klassen zu zerlegen.
main.sql
WARNINGS; SET sql_mode = 'ANSI,TRADITIONAL';
DROP SCHEMA S; CREATE SCHEMA S; USE S;CREATE TABLE SCHUELER ( NAME VARCHAR ( 255 ), RUFNUMMER VARCHAR ( 255 ), KLASSE VARCHAR ( 255 ));
INSERT INTO SCHUELER ( NAME, RUFNUMMER, KLASSE ) VALUES ( 'Rosa', '324', '7A' );
INSERT INTO SCHUELER ( NAME, RUFNUMMER, KLASSE ) VALUES ( 'Brigitte', '523', '7A' );
INSERT INTO SCHUELER ( NAME, RUFNUMMER, KLASSE ) VALUES ( 'Uwe', '245', '7A' );
INSERT INTO SCHUELER ( NAME, RUFNUMMER, KLASSE ) VALUES ( 'Matthias', '522', '7B' );
INSERT INTO SCHUELER ( NAME, RUFNUMMER, KLASSE ) VALUES ( 'Gertken', '423', '7B' );
INSERT INTO SCHUELER ( NAME, RUFNUMMER, KLASSE ) VALUES ( 'Elke', '811', '7B' );SELECT NAME, RUFNUMMER FROM SCHUELER WHERE KLASSE = '7A';
SELECT NAME, RUFNUMMER FROM SCHUELER WHERE KLASSE = '7B';
- Protokoll
+----------+-----------+
| NAME | RUFNUMMER |
+----------+-----------+
| Rosa | 324 |
| Brigitte | 523 |
| Uwe | 245 |
+----------+-----------+
+----------+-----------+
| NAME | RUFNUMMER |
+----------+-----------+
| Matthias | 522 |
| Gertken | 423 |
| Elke | 811 |
+----------+-----------+
Begründung: Wenn man Daten mit gleicher Struktur auf verschiedene Tabellen verteilt, wird es nach den Erfahrungen des Autors in der Regel schwieriger, für typische Bedürfnisse Abfragen zu erstellen und zu warten.
Übungsfragen
? Übungsfrage
Welche Zeilen enthält die Tabelle, die vom folgenden Skript ausgegeben wird?
main.sql
WARNINGS; SET sql_mode = 'ANSI,TRADITIONAL';
DROP SCHEMA S; CREATE SCHEMA S; USE S;CREATE TABLE A ( FARBE VARCHAR ( 255 ));
INSERT INTO A ( FARBE ) VALUES ( 'Rot' );
INSERT INTO A ( FARBE ) VALUES ( 'Gruen' );CREATE TABLE B ( FARBE VARCHAR ( 255 ));
INSERT INTO B ( FARBE ) VALUES ( 'Rot' );
INSERT INTO B ( FARBE ) VALUES ( 'Blau' );SELECT FARBE FROM A
UNION
SELECT FARBE FROM B;