2017-10-11 4 views
4

ich eine Anwendung in F # bin Modellierung und ich stieß auf eine Schwierigkeit bei dem Versuch, die Datenbanktabellen für die folgenden rekursiven Typ zu konstruieren:F # rekursive Typen SQL-Tabellen

type Base = 
    | Concrete1 of Concrete1 
    | Concrete2 of Concrete2 
and Concrete1 = { 
    Id : string 
    Name : string } 
and Concrete2 = { 
    Id : string 
    Name : string 
    BaseReference : Base } 

Die Lösung, die ich für die habe Moment (ich habe gefunden Inspiration hier http://www.sqlteam.com/article/implementing-table-inheritance-in-sql-server) ist:

enter image description here

ich habe zwei Anliegen mit dieser Lösung:

  1. Es wird Zeilen in der Basistabelle geben, obwohl das in meinem Modell nicht sinnvoll ist. Aber damit kann ich leben.

  2. Es scheint, dass Abfragen, um alle Informationen über BaseReference von Concrete2 zu finden, komplex sein werden, da ich die Rekursivität des Typs und der verschiedenen konkreten Tabellen berücksichtigen muss. Darüber hinaus muss das Hinzufügen eines neuen konkreten Typs zum Modell diese Abfragen modifizieren. Es sei denn natürlich gibt es ein Äquivalent zum F # Schlüsselwort match in SQL.

Am Sorgen ich zu viel über diese Bedenken? Oder gibt es eine bessere Möglichkeit, diesen rekursiven F # -Typ in SQL-Tabellen zu modellieren?

+1

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? –

+1

@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

Antwort

0

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.