SQL2005:Vinculando uma tabela a várias tabelas e mantendo a integridade da referência?
-
09-06-2019 - |
Pergunta
Aqui está uma simplificação do meu banco de dados:
Table: Property Fields: ID, Address Table: Quote Fields: ID, PropertyID, BespokeQuoteFields... Table: Job Fields: ID, PropertyID, BespokeJobFields...
Depois temos outras tabelas que se relacionam com o Citar e Trabalho tabelas individualmente.
agora preciso adicionar um Mensagem tabela onde os usuários podem gravar mensagens telefônicas deixadas pelos clientes sobre Trabalhos e Cotações.
Eu poderia criar duas tabelas idênticas (Mensagem de citação e Mensagem de trabalho), mas isso viola o princípio DRY e parece confuso.
eu poderia criar um Mensagem mesa:
Table: Message Fields: ID, RelationID, RelationType, OtherFields...
Mas isso me impede de usar restrições para reforçar minha integridade referencial.Também posso prever que isso criará problemas no lado do desenvolvimento usando Linq to SQL posteriormente.
Existe uma solução elegante para esse problema ou terei que hackear algo juntos?
Queimaduras
Solução
Crie uma tabela Message, contendo um MessageId exclusivo e as diversas propriedades que você precisa armazenar para uma mensagem.
Table: Message
Fields: Id, TimeReceived, MessageDetails, WhateverElse...
Crie duas tabelas de links – QuoteMessage e JobMessage.Eles conterão apenas dois campos cada, chaves estrangeiras para Cotação/Trabalho e Mensagem.
Table: QuoteMessage
Fields: QuoteId, MessageId
Table: JobMessage
Fields: JobId, MessageId
Dessa forma, você definiu as propriedades de dados de uma Mensagem em um único local (facilitando a extensão e a consulta em todas as mensagens), mas também possui a integridade referencial vinculando Cotações e Trabalhos a qualquer número de mensagens.Na verdade, tanto uma cotação quanto um trabalho podem estar ligados ao mesmo mensagem (não tenho certeza se isso é apropriado para o seu modelo de negócios, mas pelo menos o modelo de dados oferece essa opção).
Outras dicas
A única outra maneira que consigo pensar é ter uma tabela Message base, com um Id e um TypeId.Suas subtabelas (QuoteMessage e JobMessage) fazem referência à tabela base em MessageId e TypeId - mas também possuem CHECK CONSTRAINTS para impor apenas o MessageTypeId apropriado.
Table: Message
Fields: Id, MessageTypeId, Text, ...
Primary Key: Id, MessageTypeId
Unique: Id
Table: MessageType
Fields: Id, Name
Values: 1, "Quote" : 2, "Job"
Table: QuoteMessage
Fields: Id, MessageId, MessageTypeId, QuoteId
Constraints: MessageTypeId = 1
References: (MessageId, MessageTypeId) = (Message.Id, Message.MessageTypeId)
QuoteId = Quote.QuoteId
Table: JobMessage
Fields: Id, MessageId, MessageTypeId, JobId
Constraints: MessageTypeId = 2
References: (MessageId, MessageTypeId) = (Message.Id, Message.MessageTypeId)
JobId = Job.QuoteId
O que isso traz para você, em comparação com apenas uma tabela JobMesssage e QuoteMessage?Ele eleva uma Mensagem a um cidadão de primeira classe, para que você possa ler todas as Mensagens de uma única tabela.Em troca, o caminho de consulta de uma mensagem até sua cotação ou trabalho relevante leva mais 1 adesão.Depende do fluxo do seu aplicativo, se isso é uma boa troca ou não.
Quanto a duas tabelas idênticas que violam o DRY - eu não ficaria preso a isso.No design de banco de dados, trata-se menos de DRY e mais de normalização.Se as duas coisas que você está modelando têm os mesmos atributos (colunas), mas na verdade são coisas diferentes (tabelas) - então é razoável ter várias tabelas com esquemas semelhantes.Muito melhor do que o inverso de misturar coisas diferentes.
@queimaduras
A resposta de Ian (+1) está correta [Veja a nota].Usando uma tabela muitos para muitos QUOTEMESSAGE
juntar-se QUOTE
para MESSAGE
é o modelo mais correto, mas deixará órfão MESSAGE
registros.
Este é um daqueles cru casos em que um gatilho pode ser usado.No entanto, é necessário ter cautela para garantir que o único MESSAGE
registro não pode ser associado a um QUOTE
e um JOB
.
create trigger quotemessage_trg
on quotemessage
for delete
as
begin
delete
from [message]
where [message].[msg_id] in
(select [msg_id] from Deleted);
end
Nota para Ian, acho que há um erro de digitação na definição da tabela para JobMessage
, onde as colunas devem estar JobId, MessageId
(?).Eu editaria sua citação, mas posso levar alguns anos para ganhar esse nível de reputação!
Por que não ter apenas os campos QuoteId e JobId na tabela de mensagens?Ou uma mensagem deve ser referente a uma cotação ou a um trabalho, e não a ambos?