В клоне StackOverflow какое отношение должна иметь таблица комментариев к вопросам и ответам?
-
13-09-2019 - |
Вопрос
В приложении, похожем на 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
);
Решение
Я бы выбрал подход 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:"Наследование в реляционных базах данных."
Может быть полезно использовать 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 склоняется к тому, чтобы реализовать модель данных на уровне приложения.Но без ограничений, обеспечивающих целостность базы данных, вы рискуете, что ошибки в вашем приложении или специальные запросы инструмента запросов могут нанести ущерб целостности данных.
Другие советы
В социальных сетях, которые я создаю, я делаю что-то немного другое.Если подумать, комментарий можно прикрепить практически к любому объекту на сайте.Это может быть сообщение в блоге, ветка или сообщение на форуме, статья, чья-то фотография, профиль человека, поставщик услуги и т. д.По этой причине я создаю таблицу SystemObjects, содержащую тип объекта (ссылка на таблицу).По большей части я создаю записи для сущностей моей системы, которые будут принимать комментарии... но они сопоставляются непосредственно с моими таблицами.В таблице SystemObjects хранится SystemObjectID и понятное имя для дальнейшего использования (таблица поиска).
После этого я создаю таблицу комментариев, в которой есть ссылка на SystemObjectID, указывающая мне, в какой таблице следует искать.Затем я также размещаю SystemObjectRecordID, который сообщает мне, какой PK указанной таблицы меня интересует (вместе со всеми стандартными данными комментариев).
Я использую это понятие таблицы SystemObject для многих других общих, далеко идущих концепций на моих сайтах.Подумайте о тегах, рейтингах, комментариях и любых других висячих вещах, которые могут быть прикреплены к вашему сайту и объединены для быстрого использования.
Подробнее об этом читайте в моей книге ASP.NET 3.5 Социальные сети.
Вы можете создать одну таблицу комментариев с двумя внешними ключами: один для вопросов.questionID, а другой для ответов.ответид.
Вам потребуются две таблицы доменов, которые объединяют отношения CommentsForQuestions и CommentsForAnswers.По сути, для этой цели вам понадобится создать 5 таблиц:
Questions
Answers
Comments
CommentsForQuestions (relates comments to questions)
CommentsForAnswers (relates comments to answers)
Единственная проблема заключается в том, что она уступает идее сообщений, поскольку ссылочная целостность не так сильна.Я могу гарантировать, что CommentsForQuestions соединяется с комментарием и вопросом, но я не могу предотвратить соединение вопроса и ответа с одним и тем же комментарием.
Отношения внешнего ключа;вы можете либо иметь вопросы и ответы, либо комментарии иметь столбец внешнего ключа как для вопросов, так и для ответов (и сделать эти столбцы эксклюзивными).
Лично я бы выбрал подход Posts.
Редактировать:Если подумать, есть третий подход, который может сработать;у вас может быть таблица комментариев, а затем просто таблица связей, которая связывает комментарии либо с вопросом, либо с ответом (поэтому комментарии будут иметь идентификатор и комментарий, а таблица соединений будет иметь CommentID, AnswerID и QuestionID ).Или вы можете иметь только таблицу «Комментарии», затем таблицу связей «Ответ-комментарий» и отдельную таблицу связей «Вопрос-комментарий».
Я могу придумать два способа.
Сначала используйте другой столбец в таблице комментариев, чтобы указать, относится ли комментарий к вопросу или ответу.Таким образом, PK таблицы комментариев становится тем, где PostID является внешним ключом для вопроса или ответа, а PostType может иметь что-то вроде 1 = Вопрос и 2 = Ответ.
Во-вторых, используйте таблицу взаимосвязей для каждого вопроса и ответа.Итак, у вас есть таблицы «Вопрос», «Ответ», «Комментарий», «ВопросКомментарий» и «ОтветКомментарий».
Допустим, первичным ключом таблиц «Вопрос», «Ответ», «Комментарий» являются «Идентификатор вопроса», «Идентификатор ответа» и «Идентификатор комментария» соответственно.Тогда столбцы QuestionComment будут иметь вид [QuestionID, CommentID].Аналогично, столбцы AnswerComment будут иметь вид [AnswerID, CommentID].