Teil 1: Encoding Algrebraic Datentypen in relationalen Tabellen
Ich habe mit dieser Sache oft zu kämpfen. Ich habe schließlich den Schlüssel zur Modellierung algebraischer Datentypen in relationalen Tabellen entdeckt: Check constraints.
Mit einer Check-Einschränkung können Sie einen gemeinsamen Tisch für alle Mitglieder Ihrer polymorphen Typ verwenden und dennoch die invariante jedes Mitglieds erzwingen.
Betrachten Sie das folgende SQL-Schema:
CREATE TABLE ConcreteType (
Id TINYINT NOT NULL PRIMARY KEY,
Type VARCHAR(10) NOT NULL
)
INSERT ConcreteType
VALUES
(1,'Concrete1'),
(2,'Concrete2')
CREATE TABLE Base (
Id INT NOT NULL PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
ConcreteTypeId TINYINT NOT NULL,
BaseReferenceId INT NULL)
GO
ALTER TABLE Base
ADD CONSTRAINT FK_Base_ConcreteType
FOREIGN KEY(ConcreteTypeId)
REFERENCES ConcreteType(Id)
ALTER TABLE Base
ADD CONSTRAINT FK_Base_BaseReference
FOREIGN KEY(BaseReferenceId)
REFERENCES Base(Id)
Einfach, nicht wahr?
Wir haben Bedenken Nr. 1 von sinnlosen Daten in der Tabelle behandelt, die die abstrakte Basisklasse darstellt, indem wir diese Tabelle eliminieren. Wir haben auch die Tabellen, die zur Modellierung jedes Betontyps verwendet wurden, kombiniert und stattdessen alle Base
Instanzen - unabhängig von ihrem konkreten Typ - in derselben Tabelle gespeichert.
As-ist, wird dieses Schema den Polymorphismus Ihrer Base
Typ nicht einschränken. So wie es ist, ist es möglich, Zeilen von ConcreteType1
mit einer Nicht-Null BaseReferenceId
oder Zeilen von ConcereteType2
mit einer Null BaseReferenceId
einzufügen. Es gibt nichts, was Sie daran hindert, ungültige Daten einzufügen. Sie müssen also sehr sorgfältig mit Ihren Einfügungen und Änderungen umgehen. Diese
ist, wo der Check-Einschränkung wirklich glänzt.
ALTER TABLE Base
ADD CONSTRAINT Base_Enforce_SumType_Properties
CHECK
(
(ConcreteTypeId = 1 AND BaseReferenceId IS NULL)
OR
(ConcreteTypeId = 2 AND BaseReferenceId IS NOT NULL)
)
Der Check-Constraint Base_Enforce_SumType_Properties
definiert die Invarianten für jeden konkreten Typ, Ihre Daten auf Insert und Update zu schützen. Gehen Sie voran und führen Sie alle DDL aus, um die Tabellen ConcreteType
und Base
in Ihrer eigenen Datenbank zu erstellen.Versuchen Sie dann, Zeilen in Base
einzufügen, die die in der Check-Einschränkung beschriebenen Regeln aufheben. Du kannst nicht! Schließlich hält Ihr Datenmodell zusammen.
Um Bedenken 2 zu adressieren: Jetzt, da alle Mitglieder Ihres Typs in einer einzigen Tabelle (mit Invarianten erzwungen) sind, werden Ihre Abfragen einfacher. Sie brauchen nicht einmal "äquivalent zum match
F # Schlüsselwort in SQL". Das Hinzufügen eines neuen Betonentyps ist so einfach wie das Einfügen einer neuen Zeile in die Tabelle ConcreteType
, das Hinzufügen neuer Eigenschaften als Spalten in der Tabelle Base
und das Ändern der Abhängigkeit, um neue Invarianten wiederzugeben.
Teil 2: Encoding hierarchische (sprich: rekursiv) Beziehungen in SQL Server
Teil Besorgnis # 2 Ich denke über die Komplexität der über die ‚Eltern-Kind‘ Beziehung Abfrage, die zwischen ConcreteType2
und Base
existiert . Es gibt viele Möglichkeiten, sich dieser Art von Abfrage zu nähern, und um eine auszuwählen, benötigen wir einen bestimmten Anwendungsfall.
Beispiel Anwendungsfall: Wir möchten jede einzelne Base
Instanz abfragen und ein Objektdiagramm erstellen, das jede Zeile enthält. Das ist einfach; Wir brauchen nicht einmal eine Verbindung. Wir brauchen nur eine veränderbare Dictionary<int,Base>
mit Id
als Schlüssel verwendet werden.
Es wäre viel zu sein, in hier zu gehen, aber es ist etwas zu beachten: Es gibt einen MSSQL-Datentyp HierarchyID
(docs) benannt, der das Muster ‚Pfad materialisierte‘ implementiert, einfachere Modellierung von Hierarchien wie die Ihre zu ermöglichen. Sie könnten versuchen, HierarchyID
anstelle von INT
auf Ihren Base.ID
/Base.BaseReferenceID
Spalten zu verwenden.
Ich hoffe, das hilft.
Es scheint mir, dass Ihr Typ Base in etwa entspricht einer nicht leeren Liste von (ID, Name) Tupel? Ist das so, oder hast du das Beispiel getrimmt, um es zu fragen? –
@RobertNielsen: Zuerst trimme ich das Beispiel, um zu fragen, aber jetzt denke ich werde ich so gehen (eine Liste von (Id, Type)) und ich werde eine weitere Tabelle erstellen, die die allgemeinen Informationen für die konkreten Typen enthält, eine BasedInformation-Tabelle, die jede konkrete Tabelle mit einem Fremdschlüssel referenziert. Ich mache das, weil sonst die Basistabelle die gleichen Informationen zwischen den Zeilen enthält. – Mario