Bietet Rezepte, Lösungen und ausführliche Beispiele rund um gesundes SQL und zufriedene Datenbanken.
SQL basiert eigentlich immer auf Mengenoperationen. Häufig ist damit ein JOIN gemeint. Damit können Daten mit Hilfe von Fremdschlüsseln quasi "horizontal" verknüpft werden. Es gibt aber eine Reihe von Befehlen, die eine Art von "vertikalen" JOIN durchführen. Dabei wird auch kein Schlüsselfeld benötigt. Gemeint ist hierbei z. B. der UNION Befehl. Damit können typ-gleiche Datenmengen miteinander verglichen und als eine Gesamtergebnismenge dargestellt werden.
E-Mail Adressen sollen aus verschiedenen Tabellen mit verschiedenen Spaltennamen und verschiedenen Strukturen verglichen werden. Zudem müssen identische E-Mail gefunden und die Datenquelle ermittelt werden, um eventuell Dupletten zu finden.
* MySQL 5.1.30
Die Datenbank-Tabellen hierfür sehen wie folgt aus:
/**
* Kunden
*/
CREATE TABLE kunde (
id INT NOT NULL,
name1 VARCHAR(50) NOT NULL,
name2 VARCHAR(50) NOT NULL,
strasse VARCHAR(50) NOT NULL,
plz INT NOT NULL,
email VARCHAR(100) NOT NULL,
PRIMARY KEY (id)
);
/**
* Rechnungen
*/
CREATE TABLE rechnung (
id INT NOT NULL,
kunde_id INT NOT NULL,
rechnung_nr INT NOT NULL,
betrag DEC(10,2) NOT NULL,
datum DATE NOT NULL,
email VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
CONSTRAINT fk_rechnung_01
FOREIGN KEY (kunde_id)
REFERENCES kunde (id)
);
/**
* Sonstige Adressen
*/
CREATE TABLE adresspool (
id INT NOT NULL,
anschrift1 VARCHAR(50) NOT NULL,
anschrift2 VARCHAR(50),
strasse VARCHAR(50),
plz VARCHAR(10),
email VARCHAR(100) NOT NULL,
PRIMARY KEY (id)
);
Ein paar Testdaten für die folgenden SQL-Abfragen zur Ermittlung der doppelten E-Mail-Adressen.
/**
* Kunden
*/
INSERT INTO kunde VALUES
( 1, 'Frank', 'Muster-1', 'Strasse-1', 1, '[email protected]'),
( 2, 'Frieda', 'Muster-2', 'Strasse-2', 2, '[email protected]'),
( 3, 'Frodo', 'Muster-3', 'Ringstrasse-3', 3, '[email protected]');
/**
* Rechnungen
*/
INSERT INTO rechnung VALUES
( 1, 1, 10, 100, '2010-08-01', '[email protected]'),
( 2, 1, 11, 80, '2010-08-02', '[email protected]'),
( 3, 3, 12, 42, '2010-08-03', '[email protected]');
/**
* Sonstige Adressen
*/
INSERT INTO adresspool VALUES
( 1, 'Amigo', 'Adress-1', NULL, 'lala', '[email protected]'),
( 2, 'Alma', 'Adress-2', 'hm', '99', '[email protected]'),
( 3, 'Frida', 'Adress-3', '.', '99', '[email protected]');
Als erstes möchten wir eine bestimmte E-Mail-Adresse in allen drei Tabellen auf einmal suchen. UNION verbindet die Tabellen zu einem Datenpool, über den dann die Abfrage läuft.
/**
* UNION verbindet drei Tabellen
*/
SELECT datenpool.email
FROM ( SELECT email FROM kunde
UNION
SELECT email FROM rechnung
UNION
SELECT email FROM adresspool
) datenpool
WHERE datenpool.email = '[email protected]';
+-----------------------+
| email |
+-----------------------+
| [email protected] |
+-----------------------+
1 row in set (0.00 sec)
Nun wissen wir zwar, dass die gesuchte E-Mail Adresse in einer der drei Tabellen existiert, aber nicht wie oft und in welcher Tabelle. Ein kurzer Blick auf die Testdaten zeigt weiterhin, dass die gesuchte E-Mail eigentlich mehrfach vorhanden ist. Der UNION Befehl entfernt doppelte Zeilen in der Ergebnismenge ähnlich einem DISTINCT. Mit UNION ALL werden alle Treffer (auch identische) ausgegeben.
/**
* UNION ALL verbindet drei Tabellen
*/
SELECT datenpool.email
FROM ( SELECT email FROM kunde
UNION ALL
SELECT email FROM rechnung
UNION ALL
SELECT email FROM adresspool
) datenpool
WHERE datenpool.email = '[email protected]';
+-----------------------+
| email |
+-----------------------+
| [email protected] |
| [email protected] |
+-----------------------+
2 row in set (0.00 sec)
Jetzt werden zwei Treffer gemeldet, aber wir wissen noch nicht, aus welcher Tabelle diese stammen. Die Abfrage muss folglich noch um zusätzliche Informationen erweitert werden.
/**
* UNION ALL verbindet drei Tabellen
*/
SELECT datenpool.quelle, datenpool.id,
datenpool.name, datenpool.email, datenpool.plz
FROM ( SELECT 'K' AS quelle, id, name1 AS name, email, plz FROM kunde
UNION ALL
SELECT 'R' AS quelle, id, NULL AS name, email, NULL AS plz FROM rechnung
UNION ALL
SELECT 'A' AS quelle, id, anschrift1 AS name, email, CAST(plz AS UNSIGNED INTEGER ) AS plz FROM adresspool
) datenpool
WHERE datenpool.email = '[email protected]';
ORDER BY datenpool.quelle;
+--------+----+--------+-----------------------+------+
| quelle | id | name | email | plz |
+--------+----+--------+-----------------------+------+
| A | 3 | Frida | [email protected] | 99 |
| K | 2 | Frieda | [email protected] | 2 |
+--------+----+--------+-----------------------+------+
2 rows in set, 1 warning (0.01 sec)
Die E-Mail [email protected] ist also in der Tabelle A (adresspool) und K (kunde) mit unterschiedlichen Namen vorhanden. Es könnte sich um eine Datenduplette handeln.
Der Trick bei diesem UNION über mehrere Tabellen ist, gleichen Informationen (aber unterschiedliche Spaltennamen) identische Alias zu geben und fehlende Spalten mit NULL zu ergänzen (siehe "NULL AS name" bei Tabelle rechnung). Weiterhin kann es notwendig sein, Spaltentypen anzupassen (siehe CAST(plz AS UNSIGNED INTEGER ) für die Tabelle adresspool).