Me deparei com uma situação no banco de dados com bastante frequência, em que uma determinada tabela pode ser FK para uma de várias tabelas pai diferentes. Eu já vi duas soluções para o problema, mas nenhuma delas é pessoalmente satisfatória. Estou curioso para ver outros padrões que você já viu por aí. Há uma melhor forma de fazê-lo?
Um exemplo inventado
Digamos que meu sistema tenhaAlerts
. 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, os Artigos e os Produtos estão se movendo rapidamente (ou localizados) para que o texto / dados necessários não possam ser inseridos nos Alertas após a criação de um Alerta. Dada essa configuração, eu vi duas soluções.
Nota: Abaixo de DDL é para SQL Server, mas minha pergunta deve ser aplicável a qualquer DBMS.
Solução 1 - FKeys múltiplas anuláveis
Nesta solução, a tabela vinculada a uma das muitas tabelas possui várias colunas FK (por uma questão de brevidade, o DDL abaixo não mostra a criação da FK). O BOM - Nesta solução, é bom ter chaves estrangeiras. A opção nula dos FKs torna conveniente e relativamente fácil adicionar dados precisos. A consulta BAD não é boa porque requer instruções 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' possui um FK na tabela Alert. Isso facilita a recuperação de alertas associados a um pai. No lado negativo, não há uma cadeia real do alerta para quem faz referência. Além disso, o modelo de dados permite alertas órfãos - onde um alerta não está associado a um produto, notícias ou cliente. Mais uma vez, várias 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ções? Existem soluções alternativas que você achou mais satisfatórias?
Alertable
. Isso faz algum sentido?