Eu me deparo com uma situação no banco de dados com bastante frequência em que uma determinada tabela pode FK para uma das várias tabelas pai diferentes. Já vi duas soluções para o problema, mas nenhuma é pessoalmente satisfatória. Estou curioso para saber quais outros padrões você viu por aí? Existe uma maneira melhor de fazer isso?
Um exemplo artificial
Digamos que meu sistema tenha Alerts
. Os alertas podem ser recebidos para uma variedade de objetos -- Clientes, Notícias e Produtos. Um determinado alerta pode ser para um e apenas um item. Por qualquer motivo, os clientes, artigos e produtos estão em movimento rápido (ou localizados), portanto, o texto/dados necessários não podem ser inseridos nos alertas após a criação de um alerta. Dada essa configuração, vi duas soluções.
Observação: o DDL abaixo é para SQL Server, mas minha pergunta deve ser aplicável a qualquer DBMS.
Solução 1 -- Múltiplas FKeys anuláveis
Nesta solução, a tabela vinculada a uma das muitas tabelas possui várias colunas FK (para simplificar, o DDL abaixo não mostra a criação de FK). O BOM - Nesta solução, é bom que eu tenha chaves estrangeiras. A opção nula do FK torna isso conveniente e relativamente fácil de adicionar dados precisos. O RUIM A consulta não é boa porque requer N LEFT JOINS ou N UNION para obter os dados associados. No SQL Server, especificamente o LEFT JOINS impede a criação de uma exibição indexada.
CREATE TABLE Product (
ProductID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
CONSTRAINT PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
CustomerID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
CONSTRAINT PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
NewsID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
CONSTRAINT PK_News Primary Key CLUSTERED (NewsID)
)
CREATE TABLE Alert (
AlertID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
ProductID int null,
NewsID int null,
CustomerID int null,
CONSTRAINT PK_Alert Primary Key CLUSTERED (AlertID)
)
ALTER TABLE Alert WITH CHECK ADD CONSTRAINT CK_OnlyOneFKAllowed
CHECK (
(ProductID is not null AND NewsID is null and CustomerID is null) OR
(ProductID is null AND NewsID is not null and CustomerID is null) OR
(ProductID is null AND NewsID is null and CustomerID is not null)
)
Solução 2 -- Um FK em cada Tabela Pai
Nesta solução, cada tabela 'pai' tem um FK para a tabela Alerta. Isso facilita a recuperação de alertas associados a um pai. No lado negativo, não há nenhuma cadeia real do Alerta para quem faz referência. Além disso, o modelo de dados permite alertas órfãos -- em que um alerta não está associado a um produto, notícias ou cliente. Novamente, vários LEFT JOINs para descobrir a associação.
CREATE TABLE Product (
ProductID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
AlertID int null,
CONSTRAINT PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
CustomerID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
AlertID int null,
CONSTRAINT PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
NewsID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
Name varchar(100) not null
AlertID int null,
CONSTRAINT PK_News Primary Key CLUSTERED (NewsID)
)
CREATE TABLE Alert (
AlertID int identity(1,1) not null,
CreateUTC datetime2(7) not null,
CONSTRAINT PK_Alert Primary Key CLUSTERED (AlertID)
)
Isso é apenas vida em um banco de dados de relação? Existem soluções alternativas que você achou mais satisfatórias?
Entendo a segunda solução como não aplicável, pois não oferece relacionamento de um (objeto) para muitos (alerta).
Você está preso a apenas duas soluções por causa da estrita conformidade com a 3NF.
Eu projetaria um esquema de acoplamento menor:
Ou, se o relacionamento de integridade for obrigatório, posso projetar:
De qualquer forma...
Três soluções válidas mais outra a ser considerada para relacionamentos muitos (objetos)-para-um (alerta)...
Esses apresentados, qual a moral?
Eles diferem sutilmente e têm o mesmo peso nos critérios:
Então, escolha o que é mais confortável para você.
Eu usei tabelas de junção mantidas por gatilho. a solução funciona muito bem como último recurso se a refatoração do banco de dados não for possível ou desejável. A ideia é que você tenha uma tabela que está ali apenas para lidar com as questões de RI, e todas as DRI vão contra ela.