Inspirado por uma pergunta de modelagem do Django: Modelagem de Banco de Dados com várias relações muitos-para-muitos no Django . O design do db é algo 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)
) ;
e o problema é como definir a BookAspectRating
tabela e impor a integridade referencial; portanto, não é possível adicionar uma classificação para uma (Book, Aspect)
combinação inválida.
AFAIK, CHECK
restrições complexas (ou ASSERTIONS
) que envolvem subconsultas e mais de uma tabela, que poderia resolver isso, não estão disponíveis em nenhum DBMS.
Outra idéia é usar (pseudocódigo) uma visão:
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) ;
e uma tabela que possui uma chave estrangeira para a exibição acima:
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)
) ;
Três perguntas:
Existem DBMS que permitem um (possivelmente materializado)
VIEW
com umPRIMARY KEY
?Existem DBMS que permitem que um
FOREIGN KEY
queREFERENCES
umVIEW
(e não apenas uma baseTABLE
)?Esse problema de integridade poderia ser resolvido de outra forma - com os recursos DBMS disponíveis?
Esclarecimento:
Como provavelmente não existe uma solução 100% satisfatória - e a questão do Django nem sequer é minha! - Estou mais interessado em uma estratégia geral de possível ataque ao problema, não em uma solução detalhada. Portanto, uma resposta como "no DBMS-X isso pode ser feito com gatilhos na tabela A" é perfeitamente aceitável.
fonte
Respostas:
Esta regra de negócios pode ser aplicada no modelo usando apenas restrições. A tabela a seguir deve resolver seu problema. Use-o em vez da sua visão:
fonte
TagID
outra Tag relacionada à mesma combinação BookAspect.Acho que você descobrirá que, em muitos casos, regras comerciais complexas não podem ser impostas apenas pelo modelo. Este é um daqueles casos em que, pelo menos no SQL Server, acho que um gatilho (de preferência um gatilho em vez de um gatilho) serve melhor ao seu propósito.
fonte
No Oracle, uma maneira de impor esse tipo de restrição de maneira declarativa seria criar uma exibição materializada que seja configurada para atualizar rapidamente na confirmação, cuja consulta identifica todas as linhas inválidas (ou seja,
BookAspectRating
linhas que não têm correspondênciaBookAspect_view
). Você pode criar uma restrição trivial nessa exibição materializada que seria violada se houver alguma linha na exibição materializada. Isso tem o benefício de minimizar a quantidade de dados que você precisa duplicar na visão materializada. No entanto, pode causar problemas, já que a restrição é aplicada apenas no momento em que você está comprometendo a transação - muitos aplicativos não são gravados para esperar que uma operação de consolidação possa falhar - e porque a violação da restrição pode ser um pouco difícil associar a uma linha ou tabela específica.fonte
SIRA_PRISE permite isso.
Embora o FK não seja mais chamado de "FK", mas apenas "restrição de banco de dados" e a "visualização" na verdade nem precise ser definida como uma visualização, é possível incluir apenas a expressão que define a visualização na declaração do restrição de banco de dados.
Sua restrição seria algo como
e pronto.
Na maioria dos DBMSs do SQL, no entanto, você teria que fazer o trabalho de análise sob sua restrição, determinar como ele pode ser violado e implementar todos os gatilhos necessários.
fonte
No PostgreSQL, não consigo imaginar uma solução sem envolver gatilhos, mas certamente pode ser resolvida dessa maneira (seja mantendo uma visão materializada de algum tipo ou um gatilho anterior
BookAspectRating
). Não há chaves estrangeiras referenciando uma view (ERROR: referenced relation "v_munkalap" is not a table
), muito menos uma chave primária.fonte