Inspirado en una pregunta de modelado de Django: modelado de bases de datos con múltiples relaciones de muchos a muchos en Django . El diseño db es algo así como:
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)
) ;
y el problema es cómo definir la BookAspectRating
tabla y hacer cumplir la integridad referencial, por lo que no se puede agregar una calificación para una (Book, Aspect)
combinación que no es válida.
AFAIK, las CHECK
restricciones complejas (o ASSERTIONS
) que involucran subconsultas y más de una tabla, que posiblemente podrían resolver esto, no están disponibles en ningún DBMS.
Otra idea es usar (pseudocódigo) una vista:
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) ;
y una tabla que tiene una clave externa para la vista anterior:
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)
) ;
Tres preguntas:
¿Hay DBMS que permiten un (posiblemente materializado)
VIEW
con unPRIMARY KEY
?¿Hay DBMS que permiten
FOREIGN KEY
queREFERENCES
aVIEW
(y no solo una baseTABLE
)?¿Podría resolverse este problema de integridad de otra manera, con las funciones DBMS disponibles?
Aclaración:
Dado que probablemente no haya una solución 100% satisfactoria, ¡y la pregunta de Django ni siquiera es mía! - Estoy más interesado en una estrategia general de posible ataque al problema, no en una solución detallada. Entonces, una respuesta como "en DBMS-X, esto se puede hacer con disparadores en la tabla A" es perfectamente aceptable.
fuente
Respuestas:
Esta regla de negocio se puede aplicar en el modelo utilizando solo restricciones. La siguiente tabla debería resolver su problema. Úselo en lugar de su vista:
fuente
TagID
etiqueta a otra que esté relacionada con la misma combinación de BookAspect.Creo que encontrará que en muchos casos, las reglas comerciales complejas no se pueden hacer cumplir solo a través del modelo. Este es uno de esos casos en los que, al menos en SQL Server, creo que un desencadenante (preferiblemente un desencadenante) sirve mejor a su propósito.
fuente
En Oracle, una forma de imponer este tipo de restricción de manera declarativa sería crear una vista materializada que se configure para actualizarse rápidamente en el commit cuya consulta identifica todas las filas no válidas (es decir,
BookAspectRating
filas que no tienen coincidenciaBookAspect_view
). Luego puede crear una restricción trivial en esa vista materializada que se violaría si hubiera filas en la vista materializada. Esto tiene el beneficio de minimizar la cantidad de datos que debe duplicar en la vista materializada. Sin embargo, puede causar problemas, ya que la restricción solo se aplica en el momento en que está confirmando la transacción (muchas aplicaciones no están escritas para esperar que una operación de confirmación pueda fallar) y porque la violación de la restricción puede ser algo difícil asociar con una fila particular o una tabla particular.fuente
SIRA_PRISE lo permite.
Aunque el FK ya no se llama "FK", sino solo "restricción de la base de datos", y la "vista" en realidad ni siquiera tiene que definirse como una vista, solo puede incluir la expresión que define la vista dentro de la declaración del restricción de la base de datos.
Tu restricción se vería algo así
y tu estas listo.
Sin embargo, en la mayoría de los DBMS de SQL, tendría que hacer el trabajo de análisis de su restricción, determinar cómo se puede violar e implementar todos los desencadenantes necesarios.
fuente
En PostgreSQL, no puedo imaginar una solución sin involucrar desencadenantes, pero ciertamente se puede resolver de esa manera (ya sea manteniendo una vista materializada de algún tipo o antes de activarlo
BookAspectRating
). No hay claves externas que hagan referencia a una vista (ERROR: referenced relation "v_munkalap" is not a table
), y mucho menos a una clave principal.fuente