Database design 数据库设计-文章、博客文章、照片、故事

Database design 数据库设计-文章、博客文章、照片、故事,database-design,Database Design,我正在为一个网站设计一个数据库,其中至少有4种不同的对象类型(文章、博客文章、照片、故事),每种对象都有不同的数据要求,足以保证它们自己的表。我们希望用户能够对这些类型中的任何一种发表评论。评论的数据要求很简单,与评论所涉及的内容类型无关(即评论正文和作者的电子邮件) 我希望避免为注释创建和管理4+个单独表的冗余,因此我希望能够将所有注释保存在一个表中,可能通过两列指定关系:一列指定父实体,另一列指定父行Id 但是我不明白如何实现外键,因为外键只在2个表和2个表之间建立关系(对吗?) 因此,考虑

我正在为一个网站设计一个数据库,其中至少有4种不同的对象类型(文章、博客文章、照片、故事),每种对象都有不同的数据要求,足以保证它们自己的表。我们希望用户能够对这些类型中的任何一种发表评论。评论的数据要求很简单,与评论所涉及的内容类型无关(即评论正文和作者的电子邮件)

我希望避免为注释创建和管理4+个单独表的冗余,因此我希望能够将所有注释保存在一个表中,可能通过两列指定关系:一列指定父实体,另一列指定父行Id

但是我不明白如何实现外键,因为外键只在2个表和2个表之间建立关系(对吗?)


因此,考虑到所有这些,最好的方法是什么?

在我看来,您最好有4个以上的单独的评论表。或者您可以使用联接表。一个表格包含所有评论。。。例:博客表,评论表,博客评论表。这将允许您拥有外键

Blog
--------
Blog_id
{other fields}

Blog_Comment
--------------
Blog_id
Comment_id


Comment
------------
Comment_id
{other fields}

可以在DB设计中使用超级类型/子类型来避免该问题。为图像、视频、笔记创建超级类型,然后链接到超级类型。在超级类型表中保留所有公共列

以下是几个链接,指向几个与模型类似的问题/答案:


这里有一种方法可以为您的应用程序实现超类型/子类型表

首先是超类型表。它包含所有子类型共有的所有列

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)
);
CREATE VIEW articles_all AS
SELECT P.*, A.placeholder
FROM publications P
INNER JOIN articles A ON (A.pub_id = P.pub_id)
接下来是几个子类型表

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()和外键约束可防止行引用超类型中错误类型的行。它在子类型之间有效地划分了pub_id值,确保任何给定的pub_id都可以出现在一个且仅一个子类型表中。这就是为什么在{publications.pub_id,publications.pub_type}列对上需要主键或NOTNULL唯一约束的原因

注释表很简单。假设所有子类型都具有相同的结构,则可以引用超类型

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)
);
添加一点数据

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(), 'a@b.com','You''re stupid'),
(1, now(), 'b@c.com', 'You''re stupid, too!');
现在,您可以创建一个视图来显示所有文章并解析联接。对于每个子类型,您都会执行相同的操作

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)
);
CREATE VIEW articles_all AS
SELECT P.*, A.placeholder
FROM publications P
INNER JOIN articles A ON (A.pub_id = P.pub_id)
您可能更喜欢“已发表文章”之类的名称,而不是“所有文章”

要选择一篇文章及其所有评论,只需左键连接两个表即可。(但请参见下文,您可能不会这么做的原因。)

对于web界面,您可能不会这样做,因为dbms必须返回文章的“n”个副本,其中“n”等于注释数。但在某些应用程序中这样做是有意义的。在有意义的应用程序中,您应该为每个子类型使用一个可更新视图,而应用程序代码大部分时间都使用可更新视图



超类型/子类型更常见的业务应用程序涉及“各方”(超类型)、“组织”和“个人”(非正式的子类型,即公司和人员)。地址(如上例中的“注释”)与超类型相关,因为所有子类型(组织和个人)有地址。

这是我建议的设计。最后一种可能性(以及您是否需要类似的东西取决于您的设计)是在注释表或blog/Video/Etx表中的每个条目中添加用户ID,这样可以检索特定用户的所有注释(同样,如果这符合您的设计要求,并针对特定的设计要求进行了一些可能的调整…)。用户ID的最终位置可能需要一些思考。@Snake:这不是一个坏主意,但连接不需要它,您也不需要动态SQL。(动态SQL有一些风险,如果可能,可以更好地避免。)“n”子类型的超类型/子类型设计映射到“n”+1个基表和“n”视图。每个“n”视图将超类型表连接到其中一个子类型表;然后客户端使用视图,而不是基表。(如有必要,编写触发器以允许您通过视图执行插入、更新和删除操作。)一旦你有了一个命名良好的、可更新的视图,你就不需要亲自阅读该列。这只是为了帮助SQL保持数据完整性。@Catcall:谢谢。了解视图的使用。我知道这一点,但没有充分考虑这个问题。另一个问题:我的计划是依靠Publications表来生成发布Id--然后将其复制到子类型表中,因此每个表中的值都是唯一的,因此我不需要在发布表中使用复合主键。这有意义吗?@Snake:我理解你的意思,但如果你使用SQL dbms,你真的需要该列和复合键。该字符(1)列,在超类型和每个子类型中实现,并用作复合键和外键引用的一部分,保证超类型表中的每一行都可以连接到一个且仅一个子类型表中的一行。没有它,您可以在每个子类型表中插入相同的发布id,从而使超类型/子类型设计n无用。@CatCall:回到最后一个问题:鉴于pubtype在主键中的角色,是否有性能原因或其他原因将其保留为1个字符?如果是,int会更好吗?(顺便说一句:感谢您的时间和耐心。请原谅我的“密度”在这些方面。我的经验主要是前端的,但这次我是一个人的项目;不过,幸运的是,我已经成功游说了很多时间,所以我可以仔细思考。)@Snake:CHAR(1)足够长,可以阅读