UNION in SQL (MySQL) (UNION in SQL (MySQL)), Lektion, Seite 722370
https://www.purl.org/stefan_ram/pub/union_sql (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram
SQL-Kurs

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;

 

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Eine Verbindung zur Stefan-Ram-Startseite befindet sich oben auf dieser Seite hinter dem Text "Stefan Ram".)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. Schlüsselwörter zu dieser Seite/relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram722370 stefan_ram:722370 UNION in SQL (MySQL) Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd722370, slrprddef722370, PbclevtugFgrsnaEnz Erklärung, Beschreibung, Info, Information, Hinweis,

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram.
https://www.purl.org/stefan_ram/pub/union_sql