Можно ли сделать так, чтобы поле идентификатора охватывало несколько таблиц в SQL Server? - PullRequest
5 голосов
/ 11 марта 2010

Могу ли я иметь «уникальный» (уникальный, не повторяющийся) столбец, охватывающий несколько таблиц? Например, скажем, у меня есть две таблицы: Книги и Авторы.

Authors
  AuthorID
  AuthorName
Books
  BookID
  BookTitle

Столбец BookID и столбец AuthorID являются столбцами идентификаторов. Я хочу, чтобы идентификационная часть охватывала оба столбца. Таким образом, если существует AuthorID со значением 123, то не может быть BookID со значением 123. И наоборот.

Надеюсь, это имеет смысл.

Возможно ли это?

Спасибо.

Почему я хочу это сделать? Я пишу приложение APS.NET MVC. Я создаю раздел комментариев. Авторы могут иметь комментарии. Книги могут иметь комментарии. Я хочу, чтобы можно было передать идентификатор объекта (идентификатор книги или идентификатор автора) в действие и получить действие все соответствующие комментарии. Действие не волнует, если это книга или автор или что-то еще. Звучит разумно?

Ответы [ 5 ]

5 голосов
/ 11 марта 2010

Даже если бы вы могли поместить последовательность идентификаторов в несколько таблиц, ваша таблица комментариев не сможет ссылаться на оба столбца в одном внешнем ключе.

Лучший способ сделать это с точки зрения теории проектирования реляционных баз данных - создать две таблицы комментариев. Но очевидно, что вы хотите избежать этого, возможно, по причинам повторного использования кода.

Самый простой прагматичный подход состоит в том, чтобы поместить два столбца внешнего ключа в таблицу комментариев и просто сделать один ноль, а другой - не ноль для каждого комментария.

Другой подход, который может быть лучшим компромиссом, заключается в следующем. В своем вопросе вы ссылаетесь на «идентификатор организации». Так что сделайте таблицу 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
2 голосов
/ 21 июля 2016

На самом деле, Джо Селко предлагает в этом блоге использовать пользовательскую последовательность в вашей базе данных, а затем, для любого первичного ключа желаемых таблиц, указать их значения по умолчанию, чтобы получить следующий номер из вашего пользовательскогоsequence.

Вот пример кода из его блога:

CREATE SEQUENCE Service_Ticket_Seq
 AS INTEGER
 START WITH 1
 INCREMENT BY 1
 MINVALUE 1
 MAXVALUE 100
 CYCLE;

CREATE TABLE Meats
(ticket_seq INTEGER DEFAULT NEXT VALUE FOR Service_Ticket_Seq
       PRIMARY KEY,
 meat_type VARCHAR(15) NOT NULL);

CREATE TABLE Fish
(ticket_seq INTEGER DEFAULT NEXT VALUE FOR Service_Ticket_Seq
       PRIMARY KEY,
 fish_type VARCHAR(15) NOT NULL);

INSERT INTO Meats (meat_type) VALUES ('pig');
INSERT INTO Fish (fish_type) VALUES ('squid');

select * from Meats

select * from Fish

При этом в MS SQL возможно поле идентичности, охватывающее несколько таблиц.

2 голосов
/ 11 марта 2010

Краткий ответ: нет, вы не можете этого сделать (по крайней мере, в MS SQL Server до 2008 года).

Вы можете создать новую таблицу "CommentableEntity", вставив туда свой столбец идентификацииЗатем определите внешние ключи в Authors и Books для ссылки на него как на родительскую таблицу, а затем выполните один из нескольких приемов, чтобы убедиться, что данное значение идентификатора не назначено обеим таблицам ... но это плохая идея, потому чтопостроенная вами модель данных подразумевает, что авторы и книги являются связанными видами данных, а на самом деле это не так.

Вы можете иметь отдельную таблицу Comments, иметь там столбец идентификаторов и парковать CommentIdколонка в обоих авторов и книг.Однако это ограничило бы каждую книгу и автора только одним комментарием.

Я, я бы, вероятно, добавил столбец типа "CommentorType" в таблицу комментариев и поставил там флаг, указывающий источник комментария ("А "для автора", "Б" для книги).Создайте первичный ключ для "CommentorId + CommentorType", и он должен работать достаточно хорошо - и было бы тривиально добавлять дополнительные типы комментариев по мере расширения системы.

0 голосов
/ 11 марта 2010

SQL-сервер не поддерживает это. Вы можете свернуть свой собственный с таблицей идентификаторов, но это будет больше работы, чем стоит.

Я предлагаю, чтобы ваша таблица комментариев выглядела так:

comment_id int identity
comment_type tinyint
entity_id int

comment_type указывает, принадлежит ли комментарий к книге, автору или чему-то еще, что вы добавите в будущем. entity_id - это идентификатор книги, автора, что угодно. В этой схеме не имеет значения, совпадают ли идентификаторы книг или авторов.

Или, если вы можете переключиться на оракула, используйте последовательность:)

0 голосов
/ 11 марта 2010

В качестве предложения - попробуйте использовать таблицу, такую ​​как ComentId, EntityId, isBook, Comment для комментариев. isBook является логическим типом и не так много места, чтобы получить. Ваша концепция не хороша с точки зрения отношений.

...