Bietet Rezepte, Lösungen und ausführliche Beispiele rund um gesundes SQL und zufriedene Datenbanken.
NULL oder NOT NULL ist nicht nur eine "feine" Anlehnung an das ähnliche Zitat von Shakespeare's "To be, or not to be", sondern auch eine wesentliche Eigenschaft von Tabellenspalten einer relationalen Datenbank. Speziell im deutschen Sprachraum wird NULL leider gerne mit der Ziffer Null übersetzt (oder verwechselt), was aber ziemlich am Thema vorbei geht. NULL steht in der Datenbankwelt für einen undefinierten Wert. Aber schauen wir uns anhand dieses schönen Beispiels an, was für Auswirkungen NULL oder NOT NULL haben können.
Wir haben eine schöne Kundentabelle und möchten einen Serienbrief daraus erstellen. Wie üblich, soll der Kunden dabei mit einer individuellen Anrede angesprochen werden. Die Daten kommen aus verschiedenen Quellen und deshalb haben wir uns entschieden, auf das NOT NULL Constraint bei einigen Tabellenspalten zu verzichen, damit die Datensätze trotzdem eingefügt werden können, falls ein Wert nicht vorhanden ist.
* IBM DB2 9.1
* MySQL 5.1.30
* ORACLE 10g 10.2 EE
Die Datenbank-Tabellen hierfür sehen wie folgt aus:
/**
* Adressen
*/
CREATE TABLE adressen_and_nulls (
id INT NOT NULL,
geschlecht INT NOT NULL, -- 1=männlich, 2=weiblich
titel VARCHAR(30),
name VARCHAR(30),
vorname VARCHAR(30),
PRIMARY KEY (id)
);
/**
* Adressen
*/
CREATE TABLE adressen_and_nulls (
id INT NOT NULL,
geschlecht INT NOT NULL, -- 1=männlich, 2=weiblich
titel VARCHAR2(30),
name VARCHAR2(30),
vorname VARCHAR2(30),
PRIMARY KEY (id)
);
Anbei die Testdaten für unseren Serienbrief. Da ORACLE derzeit keinen Multi-Row-Insert unterstützt, füllen wir die Tabelle mit mehreren einzelnen INSERT Befehlen.
/**
* Einfügen der Adressen
*/
INSERT INTO adressen_and_nulls VALUES ( 1, 1,NULL, 'Name-1', 'Vorname-1' );
INSERT INTO adressen_and_nulls VALUES ( 2, 2,'Dr. med.', 'Name-2', 'Vorname-2' );
INSERT INTO adressen_and_nulls VALUES ( 3, 1,'Prof.', 'Name-3', NULL );
INSERT INTO adressen_and_nulls VALUES ( 4, 2, NULL, 'Name-4', 'Vorname-4' );
INSERT INTO adressen_and_nulls VALUES ( 5, 1, 'Dipl.Ing.', NULL , 'Vorname-5' );
/**
* So sehen die Daten nun aus
*/
SELECT * FROM adressen_and_nulls
ORDER BY id;
+----+------------+-----------+--------+-----------+
| id | geschlecht | titel | name | vorname |
+----+------------+-----------+--------+-----------+
| 1 | 1 | NULL | Name-1 | Vorname-1 |
| 2 | 2 | Dr. med. | Name-2 | Vorname-2 |
| 3 | 1 | Prof. | Name-3 | NULL |
| 4 | 2 | NULL | Name-4 | Vorname-4 |
| 5 | 1 | Dipl.Ing. | NULL | Vorname-5 |
+----+------------+-----------+--------+-----------+
5 rows in set (0.03 sec)
Nun machen wir uns daran die Anreden mit Hilfe von SQL zu generieren.
/**
* Anrede erzeugen
*/
SELECT id,
CASE geschlecht
WHEN 1 THEN 'Sehr geehrter Herr ' || titel || ' ' || name || ', '
WHEN 2 THEN 'Sehr geehrte Frau ' || titel || ' ' || name || ', '
END AS anrede
FROM adressen_and_nulls
ORDER BY id;
/**
* Anrede erzeugen
*/
SELECT id,
CASE geschlecht
WHEN 1 THEN CONCAT('Sehr geehrter Herr ', titel, ' ', name, ', ' )
WHEN 2 THEN CONCAT('Sehr geehrte Frau ', titel, ' ', name, ', ' )
END AS anrede
FROM adressen_and_nulls
ORDER BY id;
Die SQL-Abfragen aus Listing 4 bzw. Listing 5 erzeugen überraschenderweise nur bei der ID 2 und 3 eine Anredezeile. Warum? Mit Hilfe des || Operators ( bzw. CONCAT bei MySQL ) werden gültige Werte mit NULL ("..ich weiß nicht was es ist..") Werten aneinander gehängt. Das Ergebnis kann dann nicht verarbeitet werden und ist dann ebenfalls undefiniert also NULL. Diese Reaktion der Datenbank ist überraschend, aber vollkommen korrekt!.
+----+--------------------------------------+
| id | anrede |
+----+--------------------------------------+
| 1 | NULL |
| 2 | Sehr geehrte Frau Dr. med. Name-2, |
| 3 | Sehr geehrter Herr Prof. Name-3, |
| 4 | NULL |
| 5 | NULL |
+----+--------------------------------------+
5 rows in set (0.01 sec)
Mit dem IS NOT NULL sicherstellen, dass nur vollständige Adressen verarbeitet werden.
[...]
FROM adressen_and_nulls
WHERE titel IS NOT NULL
AND name IS NOT NULL
ORDER BY id;
Oder mit einer Konvertierung wie COALESCE() aus NULL einen gültigen Wert wie beispielweise '' machen.
SELECT id,
CASE geschlecht
WHEN 1 THEN 'Sehr geehrter Herr ' || COALESCE(titel,'') || ' ' || COALESCE(name,'') || ', '
WHEN 2 THEN 'Sehr geehrte Frau ' || COALESCE(titel,'') || ' ' || COALESCE(name,'') || ', '
END AS anrede
FROM adressen_and_nulls
ORDER BY id;
SELECT id,
CASE geschlecht
WHEN 1 THEN CONCAT('Sehr geehrter Herr ', COALESCE(titel,''), ' ', COALESCE(name,''), ', ' )
WHEN 2 THEN CONCAT('Sehr geehrte Frau ', COALESCE(titel,''), ' ', COALESCE(name,''), ', ' )
END AS anrede
FROM adressen_and_nulls
ORDER BY id;
+----+--------------------------------------+
| id | anrede |
+----+--------------------------------------+
| 1 | Sehr geehrter Herr Name-1, |
| 2 | Sehr geehrte Frau Dr. med. Name-2, |
| 3 | Sehr geehrter Herr Prof. Name-3, |
| 4 | Sehr geehrte Frau Name-4, |
| 5 | Sehr geehrter Herr Dipl.Ing. , |
+----+--------------------------------------+
5 rows in set (0.00 sec)
Dies ist immerhin schon mal eine Teillösung, aber trotzdem werden nicht immer sinnvolle Anredezeilen erzeugt. Siehe Id = 5 ("Sehr geehrter Herr Dipl.Ing. , "). Ohne Namen ist die Anrede nicht sehr sinnvoll. Lesen wir also nur Datensätz mit einem Namen der nicht NULL ist.
[...]
FROM adressen_and_nulls
WHERE name IS NOT NULL
ORDER BY id;
+----+--------------------------------------+
| id | anrede |
+----+--------------------------------------+
| 1 | Sehr geehrter Herr Name-1, |
| 2 | Sehr geehrte Frau Dr. med. Name-2, |
| 3 | Sehr geehrter Herr Prof. Name-3, |
| 4 | Sehr geehrte Frau Name-4, |
+----+--------------------------------------+
4 rows in set (0.00 sec)
Sieht schon besser aus, obwohl an einigen Stellen noch zu viele Leerzeichen enthalten sind. Diese müssen eigentlich auch noch entfernt werden. Dann wird die SQL-Abfrage allerdings ziemlich komplex.
Zwei Dinge fallen auf. Zum einen ist es gar nicht so einfach, korrekte Anreden zu generieren, wenn die Daten nicht eine gewisse Qualität erreichen, zum anderen ist es von Vorteil, diese Datenqualität mit dem NOT NULL Constraint zu erzwingen. Dann ist zwar das Zusammenstellen (einfügen in die Tabelle) der Daten schwieriger, das Ergebnis - unsere generierten Anredezeilen - ist aber deutlich einfacher zu erreichen und qualitativ hochwertiger.
© 2002-2013 by Thomas Wiedmann : (Stand : 01.05.2012).
Powered by Zend Framework and "Yahoo! User Interface" (YUI)