即使您可以将标识序列放在多个表中,您的注释表也无法在单个外键中引用两列。
就关系数据库设计理论而言,最好的方法是创建两个注释表。但很明显,您想避免这种情况,可能是出于代码重用的原因。
最直接实用的方法是将两个外键列放在评论表上,并为每条评论设置一个为空,另一个不为空。
另一种方法,可能是最好的折衷方案,就是这样。您在问题中提到了“实体 ID”。所以制作一个实体表!那么作者和书籍和评论都可以参考该表。
编辑添加:
Philip Kelley、Ray 和(我认为)Artic 都建议通过添加一个 来修改评论表entity_id
,它可以指代book_id
或author_id
,以及某种标志(char(1)
分别是tinyint
、 和boolean
),指示其中哪些是被提及。
这不是一个好的解决方案,原因有很多,包括实用性(包括数据完整性、报告、效率)和理论性。
第一个也是最明显的问题是数据完整性问题。关系数据库系统应始终负责维护其自身数据的完整性,并且数据库设计有自然且首选的方式来执行此操作。这些机制中最重要的一种是外键系统。如果该comment.entity_id
列要同时引用book.book_id
和author.author_id
,则不能为此列创建外键。
当然,您可以检查您的 DML(插入、更新、删除)存储过程来验证引用,但这很快就会变成一团糟,因为所有三个表上的所有 DML 操作都将涉及。
这将我们引向效率问题。每当对comment
表运行查询时,它都需要连接到author
orbook
表或两者。查询计划生成系统将没有可用于优化的外键,因此它的性能很可能会降低。
然后这个方案在报告中存在问题。任何报告生成系统都会遇到这种系统的问题。当然,这对于专业程序员来说不是问题,但是任何用户临时报告都必须在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