我可以在SQL Server中创建一个包含多个表的标识字段吗?

joh*_*kes 5 sql-server identity-column

我可以在多个表中使用"标识"(唯一,非重复)列吗?例如,假设我有两个表:书籍和作者.

Authors
  AuthorID
  AuthorName
Books
  BookID
  BookTitle
Run Code Online (Sandbox Code Playgroud)

BookID列和AuthorID列是标识列.我希望标识部分跨越两列.因此,如果存在值为123的AuthorID,则不能存在值为123的BookID.反之亦然.

我希望这是有道理的.

这可能吗?

谢谢.

我为什么要这样做?我正在写一个APS.NET MVC应用程序.我正在创建一个评论部分.作者可以发表评论.书籍可以有评论.我希望能够将实体ID(书籍ID或作者ID)传递给某个操作,并让该操作提取所有相应的注释.如果它是书籍或作者或其他什么,该行动将无关紧要.声音合理吗?

Jef*_*dge 5

即使您可以将标识序列放在多个表中,您的注释表也无法在单个外键中引用这两个列.

就关系数据库设计理论而言,最好的方法是创建两个注释表.但显然,您可能希望避免这种情况,可能是出于代码重用的原因.

最直接的实用方法是在注释表上放置两个外键列,并为每个注释创建一个null,另一个不为null.

另一种方法可能是最好的妥协,就是这样.您在问题中提到"实体ID".所以制作实体表!然后作者,书籍和评论都可以参考表.

编辑添加:

菲利普·凯利,雷,和(我认为)北极都建议通过添加修改意见表entity_id,它可以指的book_id还是author_id,和某种形式的标志(char(1),tinyint,和boolean,分别),指示-这几被提到.

由于许多原因(包括数据完整性,报告,效率)和理论原因,这不是一个好的解决方案.

第一个也是最明显的问题是数据完整性问题.关系数据库系统应始终负责维护其自身数据的完整性,并且DB有自然和首选的方法来实现此目的.这些机制中最重要的一个是外键系统.如果comment.entity_id列同时引用book.book_idauthor.author_id,然后一个外键不能为此列创建.

当然,您可以检查您的DML(插入,更新,删除)存储过程以验证引用,但这很快就会变成一大堆,因为所有三个表上的所有DML操作都会涉及到.

这导致了我们的效率问题.每当对comment表运行查询时,它都需要连接到authorbook表或两者.查询计划生成系统将没有可用于优化的外键,因此其性能很可能会降低.

然后在报告中存在该方案的问题.任何报告生成系统都会遇到这种系统的问题.对于专业程序员来说,这不会是一个问题,但任何用户临时报告都必须模仿event_id这个或那个意味着背后的逻辑,这可能是一个非常糟糕的交易.也许你永远不会在这个数据库上使用报告生成工具.但话说回来,没有人知道最终会使用数据库的位置.为什么不使用系统允许任何东西?

这导致我们理论上的问题.

在关系数据库理论中,每个表中的每一行(也称为"元组")("关系变量")代表关于现实世界的命题.设计表格是决定该命题的形式.让我们看几个如何工作的例子.

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).*/
Run Code Online (Sandbox Code Playgroud)

这里很明显,所调用的列(或"属性")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). */
Run Code Online (Sandbox Code Playgroud)

这给我们买了第一个版本中最大遗漏的外键.但这仍然不是非常令人满意,除非一个评论可以同时引用一本书和一本作者(这可能是合理的).可空列是一个警告标志,表明设计出了问题,这也可能就是这种情况.如果不允许,则可能需要检查约束以避免引用任何内容的注释,或者引用书籍和作者的注释.

从理论的角度(因此,我的观点:))有一个明确的最佳选择:

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). */
Run Code Online (Sandbox Code Playgroud)

最后一个选项将提供最佳效率,数据完整性和易于报告.并且唯一的费用是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
Run Code Online (Sandbox Code Playgroud)


Phi*_*ley 3

简短的回答是:不,你不能这样做(至少在 2008 年的 MS SQL Server 中)。

您可以创建一个新表“CommentableEntity”,将您的身份列插入其中,然后在 Authors 和 Books 中定义外键以将其作为父表引用,然后执行多种技巧之一来确保给定的 ID 值没有分配给两个表...但这是一个糟糕的主意,因为您构建的数据模型意味着作者和书籍是相关类型的数据,但实际上并非如此。

您可以有一个单独的表 Comments,其中包含标识列,并在 Authors 和 Books 中放置 CommentId 列。然而,这将限制每本书和作者只能发表一条评论。

我可能会在评论表中添加一个像“CommentorType”这样的列,并在其中放置一个标志来指示评论来源(“A”代表作者,“B”代表书籍)。在“CommentorId + CommentorType”上构建一个主键,它应该工作得足够好——随着系统的扩展,添加更多类型的评论者将是微不足道的。