在我正在构建的类似于StackOverflow的应用程序中,我正在尝试确定我的Questions
,Answers
和Comments
表应该具有什么关系。
我可以让Questions
和Answers
都由一个表Posts
表示。
这将允许Comments
拥有Posts
的单个外键。
但如果Questions
和Answers
是不同的表格,Comments
应该对这些表格中的每一个都有什么关系?
更新:尽管所选答案建议采用类表继承方法,这似乎是数据库术语中的最佳方法,但Rails ORM不支持此选项。因此,在Rails中,我的模型必须使用单表继承,并且可能如下所示:
class Post < ActiveRecord::Base
end
class Question < Post
has_many :answers, :foreign_key => :parent_id
has_many :comments, :foreign_key => :parent_id
end
class Answer < Post
belongs_to :question, :foreign_key => :parent_id
has_many :comments, :foreign_key => :parent_id
end
class Comment < Post
belongs_to :question, :foreign_key => :parent_id
belongs_to :answer, :foreign_key => :parent_id
end
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :type
t.string :author
t.text :content
t.integer :parent_id
t.timestamps
end
end
def self.down
drop_table :posts
end
end
CREATE TABLE "posts" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"type" varchar(255),
"author" varchar(255),
"content" text,
"parent_id" integer,
"created_at" datetime,
"updated_at" datetime
);
答案 0 :(得分:17)
我会选择Posts方法。这是确保参照完整性的最佳方法。
如果您需要分别为Answers和Questions添加其他列,请将它们放在与Posts具有一对一关系的其他表中。
例如,在MySQL语法中:
CREATE TABLE Posts (
post_id SERIAL PRIMARY KEY,
post_type CHAR(1), -- must be 'Q' or 'A'
-- other columns common to both types of Post
UNIQUE KEY (post_id, post_type) -- to support foreign keys
) ENGINE=InnoDB;
CREATE TABLE Comments (
comment_id SERIAL PRIMARY KEY,
post_id BIGINT UNSIGNED NOT NULL,
-- other columns for comments (e.g. date, who, text)
FOREIGN KEY (post_id) REFERENCES Posts(post_id)
) ENGINE=InnoDB;
CREATE TABLE Questions (
post_id BIGINT UNSIGNED PRIMARY KEY,
post_type CHAR(1), -- must be 'Q'
-- other columns specific to Questions
FOREIGN KEY (post_id, post_type) REFERENCES Posts(post_id, post_type)
) ENGINE=InnoDB;
CREATE TABLE Answers (
post_id BIGINT UNSIGNED PRIMARY KEY,
post_type CHAR(1), -- must be 'A'
question_id BIGINT UNSIGNED NOT NULL,
-- other columns specific to Answers
FOREIGN KEY (post_id, post_type) REFERENCES Posts(post_id, post_type)
FOREIGN KEY (question_id) REFERENCES Questions(post_id)
) ENGINE=InnoDB;
这称为类表继承。本文概述了使用SQL建模继承:“Inheritance in relational databases。”
使用post_type会很有帮助,因此给定的帖子只能是一个答案或一个问题。您不希望答案和问题都引用一个给定的帖子。这就是上面post_type
列的目的。您可以使用CHECK约束来强制post_type
中的值,或者如果您的数据库不支持CHECK约束,则使用触发器。
我也做了一个可以帮助你的演讲。幻灯片位于http://www.slideshare.net/billkarwin/sql-antipatterns-strike-back。您应该阅读有关多态关联和实体属性值的部分。
如果您使用单表继承,正如您所说的那样使用Ruby on Rails,那么SQL DDL将如下所示:
CREATE TABLE Posts (
post_id SERIAL PRIMARY KEY,
post_type CHAR(1), -- must be 'Q' or 'A'
-- other columns for both types of Post
-- Question-specific columns are NULL for Answers, and vice versa.
) ENGINE=InnoDB;
CREATE TABLE Comments (
comment_id SERIAL PRIMARY KEY,
post_id BIGINT UNSIGNED NOT NULL,
-- other columns for comments (e.g. date, who, text)
FOREIGN KEY (post_id) REFERENCES Posts(post_id)
) ENGINE=InnoDB;
您可以在此示例中使用外键约束,我建议您这样做! : - )
Rails哲学倾向于将数据模型的实施放入应用程序层。但是,如果没有约束强制数据库中的完整性,您就有可能存在应用程序中的错误或查询工具的即席查询会损害数据完整性的风险。
答案 1 :(得分:4)
在我建立的社交网络中,我做了一些不同的事情。如果您考虑一下,评论可以附加到网站中的任何实体。这可以是博客文章,论坛帖子或帖子,文章,某人图片,人物简介,服务供应商等。为此我创建了一个包含对象类型(表引用)的SystemObjects表。在大多数情况下,我为我的系统的实体创建了接受注释的记录......但是这些实体直接映射到我的表。 SystemObjects表包含SystemObjectID,以及用于将来引用的友好名称(查找表)。
有了这个我然后创建一个Comment表,它有SystemObjectID引用,告诉我要查看的表。然后我还有SystemObjectRecordID,它告诉我我感兴趣的引用表的哪个PK(以及所有标准评论数据)。
我将SystemObject表的这个概念用于我网站中的许多其他通用远景概念。想想标签,评级,评论以及可能在您的网站上附加的任何其他悬挂水果,并汇总以便快速使用。
在我的书ASP.NET 3.5 Social Networking中了解更多相关信息。
答案 2 :(得分:2)
您可以使用两个外键创建一个注释表,一个用于questions.questionID,另一个用于answers.answerId
答案 3 :(得分:1)
您需要两个将这些关系结合在一起的域表:CommentsForQuestions和CommentsForAnswers。基本上你需要为此目的创建5个表:
Questions
Answers
Comments
CommentsForQuestions (relates comments to questions)
CommentsForAnswers (relates comments to answers)
这有一个问题是它不如帖子的想法,因为参照完整性不那么强。我可以认为CommentsForQuestions连接到评论和问题,但我不能阻止问题和答案连接到相同的评论。
答案 4 :(得分:1)
外键关系;你可以有QuestionComments和AnswerComments,或者你可以让评论有问题和答案的外键列(并且这些列是独家的)。
就个人而言,我会采用邮政方式。
编辑:考虑到,第三种方法可行;你可能有一个评论表,然后只有一个关联表,将评论与问题或答案相关联(因此评论将有一个ID和评论,连接表将有一个CommentID,一个AnswerID和一个QuestionID )。或者,您可以只有一个Comments表,然后有一个Answer-Comment关联表和一个单独的Question-Comment关联表。
答案 5 :(得分:1)
我有两种方法可以想到。
首先,使用Comment表中的另一列来指示评论是属于问题还是答案。因此,Comment表的PK变成了PostID是问题或答案的外键,PostType可以是1 = Question和2 = Answer。
其次,为每个问答使用关系表。所以你有一个问题,答案,评论,问题评论和答案评论表。
假设问题,答案,评论表的主键分别是QuestionID,AnswerID和CommentID。那么QuestionComment的列将是[QuestionID,CommentID]。同样,AnswerComment的列将是[AnswerID,CommentID]。