Optionen zur Beseitigung von Nullable-Spalten von einem DB-Modell (um SQL des dreiwertigen Logik zu vermeiden)?
-
28-09-2019 - |
Frage
Einige Vor einiger Zeit, ich habe durch das Buch zu lesen SQL und relationale Theorie von CJ Datum . Der Autor ist bekannt für SQL der Kritik an dreiwertigen Logik (3VL). 1)
Der Autor macht einige Stärken, warum 3VL sollte in SQL vermieden werden, jedoch hat er nicht Umriss , wie ein Datenbankmodell aussehen würde, wenn Nullable-Spalten nicht erlaubt waren, . Ich habe ein bisschen auf diesen Gedanken und haben mit den folgenden Lösungen kommen. Wenn ich andere Gestaltungsmöglichkeiten verpasst haben, würde Ich mag über sie hören!
1) Datum Kritik an SQLs 3VL wiederum wurde auch kritisiert: siehe dieses Papier von Claude Rubinson (von CJ Datum der ursprünglichen Kritik enthält).
Beispiel Tabelle:
Als Beispiel nehmen Sie die folgende Tabelle, wo wir eine Nullable-Spalte (DateOfBirth
):
# +-------------------------------------------+
# | People |
# +------------+--------------+---------------+
# | PersonID | Name | DateOfBirth |
# +============+--------------+---------------+
# | 1 | Banana Man | NULL |
# +------------+--------------+---------------+
Option 1: emulieren NULL
durch eine Fahne und ein Standardwert:
Statt die Spalte NULL festlegbaren machen, irgendein Standardwert angegeben (z.B. 1900-01-01
). Eine zusätzliche BOOLEAN
Spalte wird der Wert in DateOfBirth
festlegen, ob einfach ignoriert werden soll oder ob es tatsächlich Daten enthält.
# +------------------------------------------------------------------+
# | People' |
# +------------+--------------+----------------------+---------------+
# | PersonID | Name | IsDateOfBirthKnown | DateOfBirth |
# +============+--------------+----------------------+---------------+
# | 1 | Banana Man | FALSE | 1900-01-01 |
# +------------+--------------+----------------------+---------------+
Option 2: eine Spalte von -zulässige in eine separate Tabelle Drehen:
Die Nullable-Säule wird durch eine neue Tabelle (DatesOfBirth
) ersetzt. Wenn ein Datensatz für diese Spalte keine Daten hat, wird es keinen Eintrag in der neuen Tabelle sein:
# +---------------------------+ 1 0..1 +----------------------------+
# | People' | <-------> | DatesOfBirth |
# +------------+--------------+ +------------+---------------+
# | PersonID | Name | | PersonID | DateOfBirth |
# +============+--------------+ +============+---------------+
# | 1 | Banana Man |
# +------------+--------------+
Während dies wie die bessere Lösung scheint, würde dies möglicherweise in vielen Tabellen, dass Bedarf für eine einzelne Abfrage verbunden werden. Da OUTER JOIN
s nicht erlaubt werden (weil sie NULL
in der Ergebnismenge einführen würde), werden alle notwendigen Daten möglicherweise nicht mehr konnte mit nur einer einzigen Abfrage abgerufen werden wie zuvor.
Frage:
Gibt es noch andere Möglichkeiten zur Beseitigung von NULL
(und wenn ja, was sind sie)?
Lösung
Ich habe seit Kollegen Hugh Darwen dieses Problem in einer exzellenten Präsentation diskutieren „wie Informationen Griff fehlt ohne NULL Verwendung“, die auf die dritte Manifest Website .
Seine Lösung ist eine Variante auf dem zweiten Ansatz. Es ist sechste Normalform, mit Tischen sowohl Geburtsdatum zu halten und Kennungen, wo es unbekannt ist:
# +-----------------------------+ 1 0..1 +----------------------------+
# | People' | <-------> | DatesOfBirth |
# +------------+----------------+ +------------+---------------+
# | PersonID | Name | | PersonID | DateOfBirth |
# +============+----------------+ +============+---------------+
# | 1 | Banana Man | ! 2 | 20-MAY-1991 |
# | 2 | Satsuma Girl | +------------+---------------+
# +------------+----------------+
# 1 0..1 +------------+
# <-------> | DobUnknown |
# +------------+
# | PersonID |
# +============+
# | 1 |
# +------------+
von Menschen wählen dann verlangt, dass alle drei Tabellen verknüpft werden, einschließlich vorformulierten die unbekannten Geburtsdaten anzuzeigen.
Natürlich ist dies etwas theoretisch. Der Zustand von SQL wird in diesen Tagen immer noch nicht ausreichend, all dies zu handhaben fortgeschritten. Hugh Präsentation deckt diese Mängel. Eine Sache, die er erwähnt ist nicht ganz korrekt: einige Varianten von SQL unterstützt die Mehrfachzuordnung - zum Beispiel: Oracle INSERT ALL Syntax .
Andere Tipps
Ich empfehle Ihnen, gehen Sie für Ihre Wahl 2. Ich bin ziemlich sicher, Chris Datum würde auch, weil im Wesentlichen, was Sie tun, ist voll Normalisieren auf 6NF , die höchstmögliche Normalform, die Datum gemeinsam war verantwortlich einzuführen. Ich zweite empfahl die Darwen Papier auf die Verarbeitung von fehlenden Informationen.
Da Outer-Joins werden nicht erlaubt (weil sie NULL einführen würde in der Ergebnismenge), alle notwendigen Daten könnten möglicherweise nicht mehr geholt wird mit nur einer einzigen Abfrage wie zuvor.
... ist dies nicht der Fall ist, aber ich stimme mit der Frage der äußeren Verknüpfung nicht explizit in dem Darwen Papier erwähnt wird; es war das einzige, was ich will verlassen. Die explizite Antwort kann in einem anderen Datum des Buchs zu finden ...
Beachten Sie zunächst, dass Datum und Darwen eigene wirklich relationale Sprache Tutorial D hat aber man beitreten geben Sie den natürlichen Join zu sein. Die Begründung ist, dass nur ein Join-Typ tatsächlich benötigt wird.
Das Datum Buch, das ich erwähnt ist die ausgezeichnete SQL und relationale Theorie: Wie schreiben Accurate SQL-Code :
4.6: Eine Bemerkung zu Outer Join: „Relational gesprochen, [Outer Join ist] ein Art der Schrotflinte Ehe: Es zwingt Tabellen in eine Art Gewerkschaft ja, ich Bedeuten Union nicht beitreten, selbst wenn die Tabellen in Frage scheitern entsprechen den üblichen Anforderungen für die Vereinigung ... Es tut dies, in Effekt, durch Auffüllen eines oder beide der Tabellen mit NULL-Werte, bevor Sie die Vereinigung, wodurch sie zu diesen üblichen Anforderungen entsprechen Letztendlich. Aber es gibt keinen Grund, warum das Polster nicht getan werden soll mit dem richtigen Werten anstelle von NULL-Werten
Arbeiten mit dem Beispiel und den Standardwert ‚1900-01-01‘ als ‚padding‘, die Alternative zu Outer-Joins könnte wie folgt aussehen:
SELECT p.PersonID, p.Name, b.DateOfBirth
FROM Person AS p
INNER JOIN BirthDate AS b
ON p.PersonID = b.PersonID
UNION
SELECT p.PersonID, p.Name, '1900-01-01' AS DateOfBirth
FROM Person AS p
WHERE NOT EXISTS (
SELECT *
FROM BirthDate AS b
WHERE p.PersonID = b.PersonID
);
Darwen Papier Proses zwei explizite Tabellen, sagen BirthDate
und BirthDateKnown
, aber die SQL würde nicht viel anders sein, z.B. ein halb beitreten zu BirthDateKnown
anstelle des halb Unterschied zu BirthDate
oben.
Beachten Sie die oben genannten Verwendungen JOIN
und INNER JOIN
nur, weil Standard-SQL-92 NATURAL JOIN
und UNION CORRESPONDING
sind nicht weit verbreitet in der realen Leben SQL Produkte umgesetzt (kann nicht ein Zitat finden, aber IIRC Darwen war weitgehend verantwortlich für die beiden letzteren in die Standard-Herstellung ).
Ferner ist zu beachten die obige Syntax sieht langatmige nur weil SQL im Allgemeinen ist langatmig. In der reinen relationalen Algebra ist es eher wie (Pseudo-Code):
Person JOIN BirthDate UNION Person NOT MATCHING BirthDate ADD '1900-01-01' AS DateOfBirth;
Ich habe es nicht gelesen, aber es gibt einen Artikel mit dem Titel Wie Fehlende Informationen Handle Mit S-by-C auf der der Drittes Manifest Website, die von Hugh Darwen und CJ Datum laufen gelassen. Dies wird von C. J. Datum geschrieben, aber ich würde davon ausgehen, dass es eines des Artikels auf dieser Website, da es seine Meinung wahrscheinlich ähnlich ist.
Eine Alternative kann das Entity-Attribut-Wert- Modell:
entity attribute value
1 name Banana Man
1 birthdate 1968-06-20
Wenn das Geburtsdatum unbekannt war, würden Sie einfach weglassen die entsprechende Zeile.
Option 3: Onus auf dem Aufzeichnungs Schreiber:
CREATE TABLE Person
(
PersonId int PRIMARY KEY IDENTITY(1,1),
Name nvarchar(100) NOT NULL,
DateOfBirth datetime NOT NULL
)
contort Warum ein Modell null Darstellung zu ermöglichen, wenn Ihr Ziel ist es, sie zu beseitigen?
Sie können auch null
in der Ausgabe beseitigen, indem Sie COALESCE
.
SELECT personid /*primary key, will never be null here*/
, COALESCE(name, 'no name') as name
, COALESCE(birthdate,'no date') as birthdate
FROM people
Nicht alle Datenbanken unterstützen COALESCE, aber fast alle eine Ersatzoption aufgerufen haben
IFNULL(arg1, arg2)
oder etwas simular, die das gleiche tun (aber nur für 2 Argumente) .
Eine Option ist explizit Optionstypen verwenden , analog zu Haskells Maybe
Funktors.
Leider haben viele bestehenden SQL-Implementierungen schlechte Unterstützung für benutzerdefinierte algebraische Datentypen und noch ärmere Unterstützung für benutzerdefinierte Typkonstruktoren, dass Sie das wirklich tun müssen, um sauber.
Diese wieder eine Art von „null“ nur für jene Attribute, wo Sie ausdrücklich darum bitten, aber ohne null
silly dreiwertigen Logik. Nothing == Nothing
ist True
, nicht unknown
oder null
.
Unterstützung für benutzerdefinierte algebraische Typen hilft auch, wenn es ein paar Gründe für die fehlenden Informationen sind zum Beispiel eine Datenbank Äquivalent des folgenden Haskell Typ eine gute Lösung für die offensichtliche Anwendung wäre:
data EmploymentStatus = Employed EmployerID | Unemployed | Unknown
(Natürlich eine Datenbank dieser Unterstützung würde auch die mehr kompliziert als üblich Einschränkung Fremdschlüssel müssen unterstützen, die mit ihm kommt.)
Kurz dies, ich bin einverstanden mit APC 's und onedaywhen 's Antworten zu 6NF.