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

时间:2010-03-11 20:04:17

标签: sql-server identity-column

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

Authors
  AuthorID
  AuthorName
Books
  BookID
  BookTitle

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

我希望这是有道理的。

这可能吗?

感谢。

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

5 个答案:

答案 0 :(得分:5)

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

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

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

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

已编辑添加:

Philip Kelley,Ray和(我认为)Artic都建议通过添加entity_id修改评论表,该book_id可以引用author_idchar(1),以及某种标志(分别为tinyintbooleancomment.entity_id),表示其中的哪一个被引用。

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

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

当然,你可以检查你的DML(插入,更新,删除)存储过程以验证引用,但这很快就会变成一团糟,因为所有三个表上的所有DML操作都会被涉及。 / p>

这导致了我们的效率问题。每当针对author表运行查询时,它都需要连接到bookevent_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).*/ 表示这个或那个时,任何用户临时报告都必须模拟背后的逻辑,这可能是一个非常糟糕的交易。也许你永远不会在这个数据库上使用报告生成工具。但话说回来,没有人知道最终会使用数据库的位置。为什么不与系统一起工作以允许任何事情?

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

在关系数据库理论中,每个表中的每一行(a.k.a。“tuple”)(“关系变量”)代表关于现实世界的命题。设计表格是决定该命题的形式。让我们看几个如何运作的例子。

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). */

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

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

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

最后一个选项将提供最佳效率,数据完整性和易于报告。并且唯一的费用是DML存储过程需要将注释放入正确的表中,这不是什么大问题,因为他们必须知道评论所指的是什么。

如果您的计划是一次性检索书籍或作者的所有评论,那么您可以轻松地在这些表格之上创建一个视图,以再现其他设计,如果这是您想要做的。

{{1}}

答案 1 :(得分:2)

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

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

您可以拥有一个单独的表,评论,其中包含标识列,并在作者和图书中保留一条CommentId列。但是,这会限制每本书和作者只有一条评论。

我,我可能会在评论表中添加一个类似“CommentorType”的列,并在其中放置一个标志,指示评论来源(作者为“A”,书为“B”)。在“CommentorId + CommentorType”上构建一个主键,它应该运行得足够好 - 在系统扩展时添加更多类型的注释器是微不足道的。

答案 2 :(得分:2)

实际上,Joe Celko建议在this博客上使用数据库中的自定义序列,然后,对于所需表的任何主键,指定其默认值以从自定义序列中获取下一个数字。

以下是他博客中的代码示例:

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中可以使用跨越多个表的标识字段。

答案 3 :(得分:0)

作为建议 - 尝试使用ComentId,EntityId,isBook,评论等评论表。 isBook是布尔类型,并没有太多的地方可以获得。从关系的角度来看,你的概念并不好。

答案 4 :(得分:0)

SQL服务器不支持此功能。你可以使用id表滚动你自己,但那将是更多的工作,而不是它的价值。

我建议你的评论表如下:

comment_id int identity
comment_type tinyint
entity_id int

comment_type指定评论是否属于您将来添加的书籍,作者或其他内容。 entity_id是书,作者的id,无论如何。在这个方案中,书籍或作者ID是否重叠并不重要。

或者,如果您可以切换到oracle,请使用序列:)

相关问题