it-swarm.com.de

Was ist falsch an nullfähigen Spalten in zusammengesetzten Primärschlüsseln?

Oracle lässt keine NULL-Werte in einer der Spalten zu, die einen Primärschlüssel enthalten. Dies scheint auch für die meisten anderen Systeme auf Unternehmensebene zu gelten.

Gleichzeitig erlauben die meisten Systeme auch eindeutige Beschränkungen für nullfähige Spalten.

Warum können eindeutige Einschränkungen NULL-Werte haben, Primärschlüssel jedoch nicht? Gibt es einen fundamentalen logischen Grund dafür oder ist dies eher eine technische Einschränkung?

141
Roman Starkov

Primärschlüssel dienen zur eindeutigen Identifizierung von Zeilen. Dazu werden alle Teile eines Schlüssels mit der Eingabe verglichen.

Per Definition kann NULL nicht Teil eines erfolgreichen Vergleichs sein. Sogar ein Vergleich mit sich selbst (NULL = NULL) wird versagen. Dies bedeutet, dass ein Schlüssel mit NULL nicht funktioniert.

Zusätzlich ist NULL in einem Fremdschlüssel erlaubt, um eine optionale Beziehung zu markieren.(*) Wenn Sie dies auch in der PK zulassen, wird dies zunichte gemacht.


(*)Ein Wort der Vorsicht: NULL-fähige Fremdschlüssel sind kein sauberes relationales Datenbankdesign.

Wenn es zwei Entitäten A und B gibt, wobei A optional mit B verknüpft werden kann, besteht die saubere Lösung darin, eine Auflösungstabelle zu erstellen (sagen wir AB). Diese Tabelle würde A mit B verknüpfen: Wenn es ist eine Beziehung gibt, würde sie einen Datensatz enthalten, wenn es ist dann es würde nicht.

203
Tomalak

Ein Primärschlüssel definiert einen eindeutigen Bezeichner für jede Zeile in einer Tabelle: Wenn eine Tabelle einen Primärschlüssel hat, haben Sie eine garantierte Möglichkeit, eine beliebige Zeile daraus auszuwählen.

Eine eindeutige Einschränkung identifiziert nicht unbedingt jede Zeile. es gibt nur an, dass wenn eine Zeile Werte in ihren Spalten hat, dann sie müssen eindeutig sein. Dies reicht nicht aus, um jede Zeile eindeutig zu identifizieren, was ein Primärschlüssel tun muss.

56
Tony Andrews

Grundsätzlich ist mit einem NULL-Wert in einem mehrspaltigen Primärschlüssel nichts falsch. Aber eine zu haben, hat Auswirkungen, die der Designer wahrscheinlich nicht beabsichtigt hat, weshalb viele Systeme einen Fehler auslösen, wenn Sie dies versuchen.

Betrachten Sie den Fall von Modul-/Paketversionen, die als eine Reihe von Feldern gespeichert sind:

CREATE TABLE module
  (name        varchar(20) PRIMARY KEY,
   description text DEFAULT '' NOT NULL);

CREATE TABLE version
  (module      varchar(20) REFERENCES module,
   major       integer NOT NULL,
   minor       integer DEFAULT 0 NOT NULL,
   patch       integer DEFAULT 0 NOT NULL,
   release     integer DEFAULT 1 NOT NULL,
   ext         varchar(20),
   notes       text DEFAULT '' NOT NULL,
   PRIMARY KEY (module, major, minor, patch, release, ext));

Die ersten 5 Elemente des Primärschlüssels sind regelmäßig definierte Teile einer Release-Version, aber einige Pakete haben eine angepasste Erweiterung, die normalerweise keine Ganzzahl ist (wie "rc-foo" oder "Vanilla" oder "beta" oder was auch immer für jemand anderes) wem vier Felder nicht ausreichen, könnte sich das ausdenken). Wenn ein Paket keine Erweiterung hat, ist es im obigen Modell NULL, und es würde keinen Schaden anrichten, wenn die Dinge so belassen würden.

Aber was ist NULL? Es soll einen Informationsmangel darstellen, ein Unbekannter. Das heißt, vielleicht macht das mehr Sinn:

CREATE TABLE version
  (module      varchar(20) REFERENCES module,
   major       integer NOT NULL,
   minor       integer DEFAULT 0 NOT NULL,
   patch       integer DEFAULT 0 NOT NULL,
   release     integer DEFAULT 1 NOT NULL,
   ext         varchar(20) DEFAULT '' NOT NULL,
   notes       text DEFAULT '' NOT NULL,
   PRIMARY KEY (module, major, minor, patch, release, ext));

In dieser Version ist der "ext" -Teil des Tupels NICHT NULL, sondern standardmäßig eine leere Zeichenfolge, die sich semantisch (und praktisch) von einer NULL unterscheidet. Ein NULL-Wert ist ein unbekannter Wert, während ein leerer String eine bewusste Aufzeichnung von "etwas, das nicht vorhanden ist" ist. Mit anderen Worten, "leer" und "null" sind verschiedene Dinge. Es ist der Unterschied zwischen "Ich habe hier keinen Wert" und "Ich weiß nicht, was der Wert hier ist."

Wenn Sie ein Paket registrieren, dem eine Versionserweiterung fehlt , die Sie kennen , fehlt eine Erweiterung, sodass eine leere Zeichenfolge tatsächlich den richtigen Wert hat. Ein NULL-Wert wäre nur korrekt, wenn Sie nicht wüssten, ob es eine Erweiterung gibt oder nicht, oder wenn Sie wüssten, dass dies der Fall ist, aber nicht wissen, was es ist. Diese Situation ist in Systemen, in denen Zeichenfolgewerte die Norm sind, einfacher zu handhaben, da es keine andere Möglichkeit gibt, eine "leere Ganzzahl" als die Einfügung von 0 oder 1 darzustellen seine eigenen Implikationen) *.

Übrigens sind beide Methoden in Postgres gültig (da es sich um "Enterprise" -RDMBS handelt), aber die Vergleichsergebnisse können erheblich variieren, wenn Sie eine NULL in den Mix werfen - weil NULL == "Weiß nicht" so alles ist Die Ergebnisse eines Vergleichs mit einer NULL werden NULL, da Sie nichts Unbekanntes wissen können. GEFAHR! Überlegen Sie genau: Dies bedeutet, dass sich NULL-Vergleichsergebnisse durch eine Reihe von Vergleichen verbreiten . Dies kann zu subtilen Fehlern beim Sortieren, Vergleichen usw. führen.

Postgres geht davon aus, dass Sie erwachsen sind, und kann diese Entscheidung für sich selbst treffen. Oracle und DB2 gehen davon aus, dass Sie nicht bemerkt haben, dass Sie etwas Dummes tun, und werfen einen Fehler. Dies ist normalerweise das Richtige, aber nicht immer - Sie könnten tatsächlich wissen und haben in einigen Fällen keinen NULL-Wert und daher ist es korrekt, eine Zeile mit einem unbekannten Element zu hinterlassen, gegen die keine aussagekräftigen Vergleiche möglich sind.

In jedem Fall sollten Sie versuchen, die Anzahl der NULL-Felder, die Sie für das gesamte Schema zulassen, zu eliminieren, und dies doppelt, wenn es sich um Felder handelt, die Teil eines Primärschlüssels sind. In den allermeisten Fällen ist das Vorhandensein von NULL-Spalten ein Hinweis auf einen nicht normalisierten (im Gegensatz zu absichtlich de-normalisierten) Schemaentwurf und sollte vor der Annahme sorgfältig überlegt werden.

[* HINWEIS: Es ist möglich, einen benutzerdefinierten Typ zu erstellen, bei dem es sich um die Vereinigung von ganzen Zahlen und einen "unteren" Typ handelt, der semantisch "leer" im Gegensatz zu "unbekannt" bedeutet. Leider ist dies mit ein wenig Komplexität bei Vergleichsoperationen verbunden, und normalerweise lohnt es sich in der Praxis nicht, wirklich richtig zu schreiben, da Ihnen überhaupt nicht viele NULL - Werte erlaubt sein sollten. Trotzdem wäre es wunderbar, wenn RDBMS zusätzlich zu BOTTOM einen Standardtyp NULL enthalten würden, um die Angewohnheit zu vermeiden, die Semantik von "kein Wert" mit "unbekanntem Wert" zufällig zu verknüpfen. ]

44
zxq9

NULL == NULL -> false (zumindest in DBMSs)

Sie können also keine Beziehungen mit einem NULL-Wert abrufen, selbst wenn zusätzliche Spalten mit echten Werten vorhanden sind.

19
Cogsy

Die Antwort von Tony Andrews ist anständig. Die eigentliche Antwort ist jedoch, dass dies eine Konvention ist, die von der relationalen Datenbankgemeinschaft verwendet wird und KEINE Notwendigkeit darstellt. Vielleicht ist es eine gute Konvention, vielleicht auch nicht.

Der Vergleich mit NULL ergibt UNBEKANNT (3. Wahrheitswert). Wie mit Nullen angedeutet wurde, geht alle traditionelle Weisheit bezüglich der Gleichheit aus dem Fenster. Nun, so scheint es auf den ersten Blick.

Aber ich denke nicht, dass dies unbedingt so ist, und selbst SQL-Datenbanken glauben nicht, dass NULL alle Vergleichsmöglichkeiten zunichte macht.

Führen Sie in Ihrer Datenbank die Abfrage SELECT * FROM VALUES (NULL) aus. UNION SELECT * FROM VALUES (NULL)

Was Sie sehen, ist nur ein Tupel mit einem Attribut, das den Wert NULL hat. Die Union hat hier also die beiden NULL-Werte als gleich erkannt.

Beim Vergleich eines zusammengesetzten Schlüssels mit 3 Komponenten mit einem Tupel mit 3 Attributen (1, 3, NULL) = (1, 3, NULL) <=> 1 = 1 UND 3 = 3 UND NULL = NULL Das Ergebnis ist UNBEKANNT .

Wir könnten jedoch eine neue Art von Vergleichsoperator definieren, z. ==. X == Y <=> X = Y OR (X IS NULL AND Y IS NULL)

Ein solcher Gleichheitsoperator würde zusammengesetzte Schlüssel mit Nullkomponenten oder nicht zusammengesetzte Schlüssel mit Nullwerten unproblematisch machen.

4
Rami Ojares

Ich glaube immer noch, dass dies ein grundlegender/funktionaler Fehler ist, der durch eine technische Maßnahme verursacht wird. Wenn Sie ein optionales Feld haben, mit dem Sie einen Kunden identifizieren können, müssen Sie jetzt einen Dummy-Wert hineinhacken, nur weil NULL! = NULL, nicht besonders elegant, aber es ist ein "Industriestandard".

0
Adriaan Davel