Database design - articles, blog posts, photos, stories
我正在为一个网站设计一个数据库,这个网站至少有4种不同的对象类型(文章、博客文章、照片、故事),每种类型都有足够的数据需求来保证自己的表。我们希望用户能够发布这些类型的评论。评论的数据要求很简单,与评论所涉及的内容类型无关(即评论主体和作者的电子邮件)。
我希望避免为注释创建和管理4+个独立表的冗余,因此我希望能够将所有注释保存在一个表中,可能通过两列指定关系:一列指定父实体,另一列指定父行ID。
但我不知道如何实现外键,因为外键在2个表和只有2个表之间建立了关系(对吗?).
因此,考虑到所有这些,最好的方法是什么?
以下是为应用程序实现父类型/子类型表的一种方法。
首先是父类型表。它包含所有子类型通用的所有列。
1 2 3 4 5 6 | CREATE TABLE publications ( pub_id INTEGER NOT NULL PRIMARY KEY, pub_type CHAR(1) CHECK (pub_type IN ('A', 'B', 'P', 'S')), pub_url VARCHAR(64) NOT NULL UNIQUE, CONSTRAINT publications_superkey UNIQUE (pub_id, pub_type) ); |
接下来是几个子类型表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | CREATE TABLE articles ( pub_id INTEGER NOT NULL, pub_type CHAR(1) DEFAULT 'A' CHECK (pub_type = 'A'), placeholder CHAR(1) NOT NULL, -- placeholder for other attributes of articles PRIMARY KEY (pub_id, pub_type), FOREIGN KEY (pub_id, pub_type) REFERENCES publications (pub_id, pub_type) ); CREATE TABLE stories ( pub_id INTEGER NOT NULL, pub_type CHAR(1) DEFAULT 'S' CHECK (pub_type = 'S'), placeholder CHAR(1) NOT NULL, -- placeholder for other attributes of stories PRIMARY KEY (pub_id, pub_type), FOREIGN KEY (pub_id, pub_type) REFERENCES publications (pub_id, pub_type) ); |
这些子类型表中的check()和foreign key约束防止行引用父类型中的错误类型的行。它有效地在子类型之间划分pub_id值,确保任何给定的pub_id都可以出现在一个而且只能出现在一个子类型表中。这就是为什么您需要对列publications.pub_id,publications.pub_type使用主键或不使用空的唯一约束的原因。
注释表很简单。考虑到它对于所有子类型都具有相同的结构,您可以引用父类型。
1 2 3 4 5 6 7 8 | CREATE TABLE comments ( pub_id INTEGER NOT NULL REFERENCES publications (pub_id), comment_timestamp TIMESTAMP NOT NULL DEFAULT now(), commenter_email VARCHAR(10) NOT NULL, -- Only allow people who have -- really short email addresses comment_text VARCHAR(30) NOT NULL, -- Keep 'em short! PRIMARY KEY (pub_id, comment_timestamp, commenter_email) ); |
添加一点数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | INSERT INTO publications VALUES (1,'A', 'url 1 goes here'), (2,'A', 'url 2 goes here'), (3,'S', 'url 3 goes here'); INSERT INTO articles VALUES (1,'A', 'A'), (2,'A', 'B'); INSERT INTO stories VALUES (3,'S', 'A'); INSERT INTO comments VALUES (1, now(), '[email protected]','You''re stupid'), (1, now(), '[email protected]', 'You''re stupid, too!'); |
现在,您可以创建一个视图来显示所有项目并解析联接。对于每个子类型,您都要这样做。
1 2 3 4 | CREATE VIEW articles_all AS SELECT P.*, A.placeholder FROM publications P INNER JOIN articles A ON (A.pub_id = P.pub_id) |
您可能更喜欢"已发布的文章"这样的名称,而不是"所有文章"。
要选择一篇文章及其所有注释,您只需左键联接两个表。(但请看下面为什么你不这么做。)
1 2 3 4 | SELECT A.*, C.* FROM articles_all A LEFT JOIN comments C ON (A.pub_id = C.pub_id) WHERE A.pub_id = 1; |
对于Web界面,您可能实际上不需要这样做,因为DBMS必须返回文章的"n"个副本,其中"n"等于注释数。但在某些应用程序中这样做确实有意义。在有意义的应用程序中,您将为每个子类型使用一个可更新的视图,而应用程序代码大多数情况下将使用可更新的视图。
父类型/子类型的更常见业务应用程序涉及"当事方"(父类型)、"组织"和"个人"(子类型、非正式公司和人员)。与上面示例中的"注释"类似,地址与父类型相关,因为所有子类型(组织和个人)都有地址。
您可以在数据库设计中使用超级类型/子类型来避免这个问题。为图像、视频、笔记创建超级类型,然后链接到超级类型。在超级类型表中保留所有公共列。
以下是几个与模型类似的问题/答案的链接:
- 一
- 二
- 三
- 四
在我看来,你最好有4+个单独的评论表。或者你可以有连接表。一张表格显示所有评论…例如:博客表、评论表、博客评论表。这样你就可以拿到你的外用钥匙了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Blog -------- Blog_id {other fields} Blog_Comment -------------- Blog_id Comment_id Comment ------------ Comment_id {other fields} |