Даже если бы вы могли поместить последовательность идентификаторов в несколько таблиц, ваша таблица комментариев не сможет ссылаться на оба столбца в одном внешнем ключе.
Лучший способ сделать это с точки зрения теории проектирования реляционных баз данных - создать две таблицы комментариев. Но очевидно, что вы хотите избежать этого, возможно, по причинам повторного использования кода.
Самый простой прагматичный подход состоит в том, чтобы поместить два столбца внешнего ключа в таблицу комментариев и просто сделать один ноль, а другой - не ноль для каждого комментария.
Другой подход, который может быть лучшим компромиссом, заключается в следующем. В своем вопросе вы ссылаетесь на «идентификатор организации». Так что сделайте таблицу Entity! Тогда все авторы, книги и комментарии могут ссылаться на эту таблицу.
Отредактировано, чтобы добавить:
Филипп Келли, Рэй и (я думаю) Артик все предложили изменить таблицу комментариев, добавив entity_id
, который может относиться либо к book_id
или author_id
, и к какому-либо флагу ( char(1)
, tinyint
и boolean
, соответственно), которые указывают, к какому из них обращаются.
Это не очень хорошее решение по многим причинам, как прагматичным (включая целостность данных, отчетность, эффективность), так и теоретическим.
Первая и наиболее очевидная проблема - это проблема целостности данных. Система реляционных баз данных всегда должна отвечать за поддержание целостности своих собственных данных, и для этого существуют естественные и предпочтительные способы, которыми БД предназначена для этого. Одним из наиболее важных из этих механизмов является система внешних ключей. Если столбец comment.entity_id
должен ссылаться как на book.book_id
, так и author.author_id
, то для этого столбца нельзя создать внешний ключ.
Конечно, вы можете поставить галочку в хранимых процедурах DML (вставка, обновление, удаление) для проверки ссылок, но это быстро превратится в большой беспорядок, поскольку будут задействованы все операции DML на всех трех таблицах. 1030 *
И это приводит нас к проблеме эффективности. Всякий раз, когда запрос выполняется к таблице comment
, он требует объединения либо с таблицей author
или book
, либо с обеими. Система генерации плана запросов не будет иметь внешних ключей для оптимизации, поэтому ее производительность вполне может ухудшиться.
Тогда есть проблемы с этой схемой в отчете. Любая система генерации отчетов будет иметь проблемы с такой системой. Конечно, это не будет проблемой для опытных программистов, но любые специальные пользовательские отчеты должны будут смоделировать логику, когда event_id
означает то или иное, и это может быть довольно неудачной сделкой. Возможно, вы никогда не будете использовать инструменты создания отчетов в этой базе данных. Но опять же, никто не знает, где в конечном итоге будет использоваться база данных. Почему бы не работать с системой, чтобы учесть что-либо?
И это приводит нас к теоретическим проблемам.
В теории реляционных баз данных каждая строка (a.k.a. «кортеж») в каждой таблице («переменная отношения») представляет собой суждение о реальном мире. Проектирование таблицы - это выбор формы этого предложения. Давайте рассмотрим несколько примеров того, как это может работать.
comment (comment_id int, comment_type char(1), entity_id int,
user_id int, comment_text nvarchar(max), comment_date datetime)
/* comment_id identifies a comment (comment_text) that a user (user_id)
has made about a book (entity_id if comment_type = 'B') or author
(entity_id if comment_type = 'A') at a particular date and
time (comment_date).*/
Здесь ясно, что столбец (или «атрибут») с именем entity_id
выполняет двойную функцию. Это на самом деле ничего не представляет, кроме как со ссылкой на другой столбец. Это выполнимо, но неудовлетворительно.
comment (comment_id int, book_id int, author_id int, user_id int,
comment_text nvarchar(max), comment_date datetime)
/* comment_id identifies a comment (comment_text) that a user (user_id)
has made about a book (book_id if not null) or author (author_id if
not null) at a particular date and time (comment_date). */
Это покупает нам внешние ключи, которые являются самым большим упущением в первой версии. Но это все еще не очень удовлетворительно, если только один комментарий не может относиться как к книге, так и к автору (что может быть разумным). Обнуляемые столбцы - это предупреждающий знак того, что с дизайном что-то не так, и это также может иметь место Может возникнуть необходимость в проверочном ограничении, чтобы избежать комментария, который вообще ни на что не ссылается, или на книгу, и на автора, если это не разрешено.
С теоретической точки зрения (и, следовательно, с моей точки зрения :)) существует очевидный лучший вариант:
book_comment (book_comment_id int, book_id int, user_id int,
comment_text nvarchar(max), comment_date datetime)
/* book_comment_id identifies a comment (comment_text) that a
user (user_id) has made about a book (book_id) at a particular
date and time (comment_date). */
author_comment (author_comment_id int, author_id int, user_id int,
comment_text nvarchar(max), comment_date datetime)
/* author_comment_id identifies a comment (comment_text) that a
user (user_id) has made about an author (author_id) at a particular
date and time (comment_date). */
Этот последний вариант обеспечит наилучшую эффективность, целостность данных и простоту отчетности. И единственным расходом было бы то, что хранимые процедуры DML должны были бы поместить комментарии в правильные таблицы, что не составляет большого труда, так как они должны были знать, на что ссылаются комментарии.
Если вы планировали получить все комментарии сразу для книги или автора, то вы можете легко создать представление поверх этих таблиц, которое воспроизводит другие рисунки, если вы этого хотите.
create view comments as
select
book_comment_id as comment_id,
book_id as entity_id,
comment_text,
'B' as comment_type
from book_comment
union
select
author_comment_id as comment_id,
author_id as entity_id,
comment_text,
'A' as comment_type
from author_comment