Behandlung von NULL verstehen

SQLFan_2020

Neuer Benutzer
Beiträge
2
Hallo SQL-Freunde,

könnt ihr mir bitte helfen, folgendes zu verstehen:

Es geht um diese Tabelle:

CREATE TABLE IF NOT EXISTS "tblKursbelegung" (
"KursNr" TEXT,
"StudentNr" TEXT,
"Punkte" REAL,
PRIMARY KEY("KursNr","StudentNr")
);

Dabei ist es wichtig, dass Punkte gleich NULL ist, solange ein Student noch nicht bewertet wurde.

INSERT INTO "tblKursbelegung" VALUES ('A1','3',1.0);
INSERT INTO "tblKursbelegung" VALUES ('A1','4',NULL);
INSERT INTO "tblKursbelegung" VALUES ('A1','5',4.0);
INSERT INTO "tblKursbelegung" VALUES ('A1','8',NULL);
INSERT INTO "tblKursbelegung" VALUES ('A2','1',6.0);
INSERT INTO "tblKursbelegung" VALUES ('A2','3',NULL);
INSERT INTO "tblKursbelegung" VALUES ('A2','7',NULL);
INSERT INTO "tblKursbelegung" VALUES ('B1','2',5.0);
INSERT INTO "tblKursbelegung" VALUES ('B1','3',NULL);
INSERT INTO "tblKursbelegung" VALUES ('B1','6',NULL);
INSERT INTO "tblKursbelegung" VALUES ('C1','4',6.0);
INSERT INTO "tblKursbelegung" VALUES ('C1','5',NULL);
INSERT INTO "tblKursbelegung" VALUES ('C2','1',NULL);
INSERT INTO "tblKursbelegung" VALUES ('C2','2',3.0);
INSERT INTO "tblKursbelegung" VALUES ('C2','3',NULL);
INSERT INTO "tblKursbelegung" VALUES ('C2','4',NULL);
INSERT INTO "tblKursbelegung" VALUES ('C2','5',5.0);
INSERT INTO "tblKursbelegung" VALUES ('D1','7',NULL);
INSERT INTO "tblKursbelegung" VALUES ('D1','8',1.0);

Zu beachten ist, dass Student 6 in nur einem Kurs eingetragen ist und in diesem noch nicht bewertet wurde.

Die folgende Abfrage soll eine Übersicht (ähnlich einer Kreuztabellenabfrage in ACCESS) erzeugen.
Dabei sollen NULL-Werte als der Text "-" dargestellt werden.

CREATE VIEW "qryÜbersicht" AS SELECT StudentNr,
min(CASE WHEN KursNr = "A1" THEN Punkte ELSE "-" END) AS "Kurs A1",
min(CASE WHEN KursNr = "A2" THEN Punkte ELSE "-" END) AS "Kurs A2",
min(CASE WHEN KursNr = "B1" THEN Punkte ELSE "-" END) AS "Kurs B1",
min(CASE WHEN KursNr = "C1" THEN Punkte ELSE "-" END) AS "Kurs C1",
min(CASE WHEN KursNr = "C2" THEN Punkte ELSE "-" END) AS "Kurs C2",
min(CASE WHEN KursNr = "D1" THEN Punkte ELSE "-" END) AS "Kurs D1",
CASE WHEN sum(Punkte)>0 THEN sum(Punkte) ELSE "-" END AS Gesamt
FROM tblKursbelegung
GROUP BY StudentNr;

Klappt auch. Fast immer. Nur bei Student 6 im Kurs B1 wird NULL und nicht "-" angezeigt.

Meine Vermutung ist, dass wenn ein Student genau einen Kurs belegt hat und in diesem Kurs noch nicht bewertet wurde, statt "-" doch wieder NULL angezeigt wird.

Wieso ist das der Fall? Und wie lautet ein Lösung, um das zu verhindern?

Das beschrieben Verhalten habe ich mit DB Browser for SQLite beobachtet
 
Werbung:
Die Behandlung von NULL ist eigentlich ziemlich stringent, aber trotzdem tückisch.

Was hier vielleicht irritierend erscheinen mag, ist ein Mix aus mehreren "Effekten".
Die Aggregation mit MIN() ist der stärkste.
Lass das weg (Aggregation und Group by) und Du siehst genau jeden Datensatz.
Bzw. das jeweilige Ergebnis der Case When Expression.
Überall findest Du entweder NULL oder einen Wert oder "-" (den Else Case).

Für Student 6 findest Du nur in der einen Spalte überhaupt eine "echte" Angabe, NULL, die einzige Angabe für 6.

Wird das aggregiert, als einziger Wert, so bleibt es dabei, NULL. Das Minimum von NULL ist NULL, das Maximum oder die Summe ebenfalls.

Dabei gibt es 2 Effekte zu berücksichtigen.

Den einen würde ich mal "Pragmatismus" nennen: Wenn eine Spalte per SUM, MIN, MAX oder so aggregiert wird, dann werden vorkommende NULL Werte schlicht ignoriert- außer es gibt keine anderen, dann schlagen sie durch. Also die Summe von "2" und "weiß nicht" und "3" ist "5", nicht "weiß nicht". So lassen sich umstandslos große Datenmengen addieren, die halt auch Lücken haben dürfen.

Den anderen Effekt nenne ich gemein, heimtückisch, umständlich, kompliziert:
NULL im Vergleich mit einem konkreten, gegebenen Wert ist vollkommen unpragmatisch "nicht vergleichbar". Es müssen separate Vergleichoperatoren verwendet werden, um NULL "in den Griff" zu bekommen. Stichwort "is null"

Dieser letzte Fall hat nichts mit Deinem Beispiel zu tun, er ist nur ziemlich das Gegenteil von der Handhabung in der Aggregation, nicht pragmatisch. Das ist wahrscheinlich verwirrendd, deswegen führe ich es extra auf. Vielleicht hat jemand hier eine Erklärung zu diesem 'verschiedenen' Verhalten von NULL.

So, dann gibt es noch weitere Effekte, die konkret mit Deinem Beispiel und auch mit SQLite zu tun haben und es intuitiv "unverständlich" machen:
- die Verwendung von " (doppeltes Anführungszeichen) in SQL generell ist problematisch, eine Textkonstante ist mit ' einfachem Hochkomma angeben. Ein Feldnamen dagegen lieber ohne alles angeben, sonst wird er meist Case Sensitive. (Ein Ergebnis in " eingeschlossen würde also eher eine Spalte ansprechen, als einen Wert repräsentieren)
- SQLite hat einen etwas "anderen" Ansatz bei der Handhabung von Spaltentypen, Stichwort "type affinity". Das wäre vielleicht egal, wenn Du nicht auch noch "Case When" nutzen würdest und das noch schief.
- "CASE WHEN" ist problematisch, weil es nahezu dynamisch den Typ des Ergebnis "erraten" muss. Daher sollte man eigentlich nicht ohne Not den Ergebnistyp von "Case When" o.ä. mischen, in Deinem Fall eine echte Zahl (Punkte) oder "-", ein Text, der für eine optische Representation "keine Daten vorhanden"/ NULL stehen soll (genaugenommen aber ein Feldname sein könnte). Das kann zu bösen Pannen führen.

Das waren jetzt viele Worte für eine ganze Reihe von Problemchen. Einfach mal mit den Sachen spielen, auch eine andere DB nehmen und ausprobieren, wie es da läuft und welche Fehlermeldungen kommen.

Das Aggregat von NULL ist oder besser bleibt NULL, wenn es nichts anderes aggregierbares gibt, also mindestens einen weiteren Wert der nicht NULL ist.
 
Werbung:
Gut, ich denke, wenn Du die Abfrage zerlegst, ist alles nachvollziehbar. Wenn Du sie mal auf einem anderen System ausführst, wirst Du auch sehen, wie hier mit den Anführungszeichen umgegangen wird.
Ich würde es nicht "experimentieren" nennen, denn das Ergebnis ist ok, die Abfrage also kein Experiment.
Ausprobieren oder studieren trifft es besser finde ich.
 
Zurück
Oben