Inspiriert von einer Django-Modellierungsfrage: Datenbankmodellierung mit mehreren Viele-zu-Viele-Beziehungen in Django . Das DB-Design ist so etwas wie:
CREATE TABLE Book
( BookID INT NOT NULL
, BookTitle VARCHAR(200) NOT NULL
, PRIMARY KEY (BookID)
) ;
CREATE TABLE Tag
( TagID INT NOT NULL
, TagName VARCHAR(50) NOT NULL
, PRIMARY KEY (TagID)
) ;
CREATE TABLE BookTag
( BookID INT NOT NULL
, TagID INT NOT NULL
, PRIMARY KEY (BookID, TagID)
, FOREIGN KEY (BookID) REFERENCES Book (BookID)
, FOREIGN KEY (TagID) REFERENCES Tag (TagID)
) ;
CREATE TABLE Aspect
( AspectID INT NOT NULL
, AspectName VARCHAR(50) NOT NULL
, PRIMARY KEY (AspectID)
) ;
CREATE TABLE TagAspect
( TagID INT NOT NULL
, AspectID INT NOT NULL
, PRIMARY KEY (TagID, AspectID)
, FOREIGN KEY (TagID) REFERENCES Tag (TagID)
, FOREIGN KEY (AspectID) REFERENCES Aspect (AspectID)
) ;
und das Problem ist, wie die BookAspectRating
Tabelle definiert und die referenzielle Integrität erzwungen wird, sodass für eine (Book, Aspect)
ungültige Kombination keine Bewertung hinzugefügt werden kann .
AFAIK, komplexe CHECK
Einschränkungen (oder ASSERTIONS
), die Unterabfragen und mehr als eine Tabelle umfassen, die dies möglicherweise lösen könnten, sind in keinem DBMS verfügbar.
Eine andere Idee ist, eine Ansicht zu verwenden (Pseudocode):
CREATE VIEW BookAspect_view
AS
SELECT DISTINCT
bt.BookId
, ta.AspectId
FROM
BookTag AS bt
JOIN
Tag AS t ON t.TagID = bt.TagID
JOIN
TagAspect AS ta ON ta.TagID = bt.TagID
WITH PRIMARY KEY (BookId, AspectId) ;
und eine Tabelle mit einem Fremdschlüssel für die obige Ansicht:
CREATE TABLE BookAspectRating
( BookID INT NOT NULL
, AspectID INT NOT NULL
, PersonID INT NOT NULL
, Rating INT NOT NULL
, PRIMARY KEY (BookID, AspectID, PersonID)
, FOREIGN KEY (PersonID) REFERENCES Person (PersonID)
, FOREIGN KEY (BookID, AspectID)
REFERENCES BookAspect_view (BookID, AspectID)
) ;
Drei Fragen:
Gibt es DBMS, die ein (eventuell materialisiertes)
VIEW
mit a erlaubenPRIMARY KEY
?Gibt es DBMS, die ein,
FOREIGN KEY
dassREFERENCES
einVIEW
(und nicht nur eine BasisTABLE
) ermöglichen?Könnte dieses Integritätsproblem anders gelöst werden - mit verfügbaren DBMS-Funktionen?
Klärung:
Da gibt es wohl keine 100% befriedigende Lösung - und die Django-Frage ist nicht mal meine! - Ich bin mehr an einer allgemeinen Strategie für einen möglichen Angriff auf das Problem interessiert, nicht an einer detaillierten Lösung. Eine Antwort wie "in DBMS-X kann dies mit Triggern in Tabelle A erfolgen" ist daher durchaus akzeptabel.
quelle
Antworten:
Diese Geschäftsregel kann im Modell nur mit Einschränkungen erzwungen werden. Die folgende Tabelle sollte Ihr Problem lösen. Verwenden Sie es anstelle Ihrer Ansicht:
quelle
TagID
Tag in ein anderes zu ändern, das sich auf dieselbe BookAspect-Kombination bezieht.Ich denke, Sie werden feststellen, dass komplexe Geschäftsregeln in vielen Fällen nicht über das Modell allein durchgesetzt werden können. Dies ist einer der Fälle, in denen, zumindest in SQL Server, ein Auslöser (vorzugsweise ein statt eines Auslösers) Ihrer Meinung nach besser zu Ihrem Zweck passt.
quelle
In Oracle besteht eine Möglichkeit, diese Art von Einschränkung deklarativ zu erzwingen, darin, eine materialisierte Ansicht zu erstellen, die beim Festschreiben schnell aktualisiert wird und deren Abfrage alle ungültigen Zeilen identifiziert (dh
BookAspectRating
Zeilen, in denen keine Übereinstimmung bestehtBookAspect_view
). Sie können dann eine einfache Einschränkung für diese materialisierte Ansicht erstellen, die verletzt würde, wenn die materialisierte Ansicht Zeilen enthält. Dies hat den Vorteil, dass die Datenmenge, die Sie in der materialisierten Ansicht duplizieren müssen, minimiert wird. Dies kann jedoch zu Problemen führen, da die Einschränkung nur zu dem Zeitpunkt erzwungen wird, an dem Sie die Transaktion festschreiben. Viele Anwendungen sind nicht so geschrieben, dass ein Festschreibevorgang fehlschlagen könnte. Außerdem kann die Einschränkungsverletzung etwas schwerwiegend sein einer bestimmten Zeile oder einer bestimmten Tabelle zuordnen.quelle
SIRA_PRISE erlaubt das.
Obwohl die FK nicht mehr "FK" heißt, sondern nur noch "Datenbankeinschränkung" und die "Ansicht" nicht einmal mehr als Ansicht definiert werden muss, können Sie die Ansicht, die den Ausdruck definiert, einfach in die Deklaration der einschließen Datenbankeinschränkung.
Ihre Einschränkung würde ungefähr so aussehen
und du bist fertig.
In den meisten SQL-DBMS müssen Sie jedoch die Analysearbeit für Ihre Einschränkung ausführen, bestimmen, wie gegen sie verstoßen werden kann, und alle erforderlichen Trigger implementieren.
quelle
In PostgreSQL kann ich mir keine Lösung ohne Trigger vorstellen, aber sie kann sicherlich auf diese Weise gelöst werden (sei es eine materialisierte Ansicht oder ein vorheriger Trigger
BookAspectRating
). Es gibt keine Fremdschlüssel, die auf eine Ansicht verweisen (ERROR: referenced relation "v_munkalap" is not a table
), geschweige denn einen Primärschlüssel.quelle