6

我可以让一个“身份”(唯一的、非重复的)列跨越多个表吗?例如,假设我有两个表:Books 和 Authors。

Authors
  AuthorID
  AuthorName
Books
  BookID
  BookTitle

BookID 列和 AuthorID 列是标识列。我希望标识部分跨越两列。所以,如果有一个值为 123 的 AuthorID,那么就不可能有一个值为 123 的 BookID。反之亦然。

我希望这是有道理的。

这可能吗?

谢谢。

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

4

5 回答 5

5

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

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

最直接实用的方法是将两个外键列放在评论表上,并为每条评论设置一个为空,另一个不为空。

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

编辑添加:

Philip Kelley、Ray 和(我认为)Artic 都建议通过添加一个 来修改评论表entity_id,它可以指代book_idauthor_id,以及某种标志(char(1)分别是tinyint、 和boolean),指示其中哪些是被提及。

这不是一个好的解决方案,原因有很多,包括实用性(包括数据完整性、报告、效率)和理论性。

第一个也是最明显的问题是数据完整性问题。关系数据库系统应始终负责维护其自身数据的完整性,并且数据库设计有自然且首选的方式来执行此操作。这些机制中最重要的一种是外键系统。如果该comment.entity_id列要同时引用book.book_idauthor.author_id,则不能为此列创建外键。

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

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

然后这个方案在报告中存在问题。任何报告生成系统都会遇到这种系统的问题。当然,这对于专业程序员来说不是问题,但是任何用户临时报告都必须在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).*/

很明显,被调用的列(或“属性”)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
于 2010-03-11T20:19:50.660 回答
4

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

这是他博客中的代码示例:

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 中是可能的。

于 2016-07-21T15:11:05.070 回答
3

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

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

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

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

于 2010-03-11T20:21:22.260 回答
0

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

于 2010-03-11T20:17:28.840 回答
0

SQL 服务器不支持这个。您可以使用 id 表自己滚动,但这将比它的价值更多。

我建议您的评论表如下所示:

comment_id int identity
comment_type tinyint
entity_id int

comment_type 指定评论是否属于一本书、作者或您将来添加的其他内容。entity_id 是书籍、作者等的 id。在此方案中,书籍或作者 ID 是否重叠并不重要。

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

于 2010-03-11T20:22:03.747 回答