SQL2005:Связать таблицу с несколькими таблицами и сохранить целостность ссылок?
-
09-06-2019 - |
Вопрос
Вот упрощение моей базы данных:
Table: Property Fields: ID, Address Table: Quote Fields: ID, PropertyID, BespokeQuoteFields... Table: Job Fields: ID, PropertyID, BespokeJobFields...
Затем у нас есть другие таблицы, относящиеся к Цитировать и Работа столы индивидуально.
теперь мне нужно добавить Сообщение таблица, в которой пользователи могут записывать телефонные сообщения, оставленные клиентами относительно вакансий и предложений.
Я мог бы создать две одинаковые таблицы (ЦитатаСообщение и РаботаСообщение), но это нарушает принцип DRY и кажется беспорядочным.
я мог бы создать один Сообщение стол:
Table: Message Fields: ID, RelationID, RelationType, OtherFields...
Но это мешает мне использовать ограничения для обеспечения ссылочной целостности.Я также могу предвидеть, что это создаст проблемы со стороны разработчиков, использующих Linq to SQL в дальнейшем.
Есть ли элегантное решение этой проблемы, или мне в конечном итоге придется что-то взломать?
Бернс
Решение
Создайте одну таблицу Message, содержащую уникальный MessageId и различные свойства, которые необходимо сохранить для сообщения.
Table: Message
Fields: Id, TimeReceived, MessageDetails, WhateverElse...
Создайте две таблицы ссылок — QuoteMessage и JobMessage.Они будут содержать всего два поля каждое: внешние ключи к цитате/заданию и сообщению.
Table: QuoteMessage
Fields: QuoteId, MessageId
Table: JobMessage
Fields: JobId, MessageId
Таким образом, вы определили свойства данных сообщения только в одном месте (что позволяет легко расширять и выполнять запросы ко всем сообщениям), но у вас также есть ссылочная целостность, связывающая котировки и вакансии с любым количеством сообщений.Действительно, и цитата, и вакансия могут быть связаны с такой же сообщение (я не уверен, соответствует ли это вашей бизнес-модели, но, по крайней мере, модель данных дает вам такую возможность).
Другие советы
Единственный другой способ, который я могу придумать, - это иметь базовую таблицу сообщений с идентификатором и TypeId.Ваши подтаблицы (QuoteMessage и JobMessage) затем ссылаются на базовую таблицу как для MessageId, так и для TypeId, но также содержат ПРОВЕРКИ ОГРАНИЧЕНИЙ для обеспечения соблюдения только соответствующего MessageTypeId.
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
Что это дает вам по сравнению с таблицами JobMessage и QuoteMessage?Он присваивает Сообщению статус гражданина первого класса, так что вы можете читать все Сообщения из одной таблицы.Взамен ваш путь запроса от сообщения к соответствующему предложению или заданию находится на расстоянии еще одного соединения.Это отчасти зависит от потока вашего приложения, является ли это хорошим компромиссом или нет.
Что касается двух одинаковых таблиц, нарушающих DRY, то я бы на этом не зацикливался.При проектировании БД речь идет не столько о DRY, сколько о нормализации.Если две модели, которые вы моделируете, имеют одинаковые атрибуты (столбцы), но на самом деле являются разными вещами (таблицами), то разумно иметь несколько таблиц со схожими схемами.Гораздо лучше, чем наоборот — смешивать разные вещи вместе.
@бернс
Ответ Йена (+1) правильный [смотрите примечание].Использование таблицы «многие ко многим» QUOTEMESSAGE
присоединиться QUOTE
к MESSAGE
самая правильная модель, но оставит сиротой MESSAGE
записи.
Это один из тех редкий случаи, когда можно использовать триггер.Однако необходимо соблюдать осторожность, чтобы гарантировать, что единственный MESSAGE
запись не может быть связана как с QUOTE
и 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
Примечание для Яна: я думаю, что в определении таблицы допущена опечатка. JobMessage
, где должны быть столбцы JobId, MessageId
(?).Я бы отредактировал вашу цитату, но мне может потребоваться несколько лет, чтобы добиться такого уровня репутации!
Почему бы просто не иметь в таблице сообщений поля QuoteId и JobId?Или сообщение должно касаться либо предложения, либо вакансии, а не того и другого?