В клон StackOverflow, какая связь должна иметь таблица комментариев в вопросах и ответах?

В приложении, аналогичном StackOverflow, который я создаю, я пытаюсь решить, какие отношения должны иметь таблицы Questions, Answers и Comments.

Я мог бы иметь Questions и Answers обе быть представлены одной таблицей Posts.

Это позволит Comments иметь один внешний ключ для Posts.

Но если Questions и Answers являются отдельными таблицами, какие отношения должны Comments иметь к каждому из них?

ОБНОВЛЕНИЕ. Хотя выбранный ответ рекомендует использовать метод наследования класса таблицы, и это похоже на лучший подход в терминах базы данных, этот параметр не поддерживается ORM Rails. Итак, в Rails моим моделям придется использовать Single Table Inheritance и, вероятно, будет выглядеть так:

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
  );

Ответ 1

Я бы пошел с подходом Posts. Это лучший способ обеспечить ссылочную целостность.

Если вам нужны дополнительные столбцы для ответов и вопросов соответственно, поместите их в дополнительные таблицы с отношениями "один к одному" с 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 имеет тенденцию способствовать внедрению модели данных в прикладной уровень. Но без ограничений, обеспечивающих целостность в базе данных, вы рискуете, что ошибки в вашем приложении или специальные запросы из инструмента запроса могут повредить целостность данных.

Ответ 2

В социальных сетях, которые я создаю, я делаю что-то совсем другое. Если вы думаете об этом, комментарий может быть прикреплен к любому сущности на сайте. Это может быть сообщение в блоге, поток или сообщение форума, статья, изображение someones, профиль лиц, поставщик сервиса и т.д. По этой причине я создаю таблицу SystemObjects, которая содержит тип объекта (ссылка на таблицу). По большей части я создаю записи для сущностей моей системы, которые будут принимать комментарии... но они отображаются непосредственно в мои таблицы. В таблице SystemObjects находится SystemObjectID и дружественное имя для ссылки в будущем (таблица поиска).

С этим на месте я создаю таблицу комментариев, в которой есть ссылка SystemObjectID, чтобы рассказать мне, в какую таблицу искать. Затем я также размещаю SystemObjectRecordID, который сообщает мне, какой PK таблицы, на которую я заинтересован (вместе с все стандартные данные комментариев).

Я использую это понятие таблицы SystemObject для многих других общих далеко идущих концепций на своих сайтах. Подумайте о тегах, рейтингах, комментариях и любых других висящих фруктах, которые могут быть прикреплены на вашем сайте и объединены для быстрого использования.

Подробнее об этом читайте в моей книге ASP.NET 3.5 Социальные сети.

Ответ 3

Вы можете создать одну таблицу комментариев с двумя внешними ключами, один для вопросов .questionID, а другой - для answer.answerId

Ответ 4

Вам понадобятся две таблицы домена, которые связывают отношения вместе с комментариями ForQuestions и CommentsForAnswers. В основном вам понадобится создать 5 таблиц для этой цели:

Questions
Answers
Comments
CommentsForQuestions (relates comments to questions)
CommentsForAnswers (relates comments to answers)

Единственная проблема заключается в том, что она уступает идее сообщений, поскольку ссылочная целостность не такая сильная. Я могу гарантировать, что комментарииForQuestions соединяются с комментарием и вопросом, но я не могу предотвратить вопрос и ответ от подключения к тому же комментарию.

Ответ 5

Отношение внешних ключей; вы можете либо иметь комментарии и комментарии, либо иметь комментарии в столбце "Внешний ключ" для обоих вопросов и ответов (и чтобы эти столбцы были эксклюзивными).

Лично я бы пошел с подходом Posts.

Изменить: при рассмотрении есть третий подход, который может работать; у вас может быть таблица комментариев, а затем просто есть таблица ассоциаций, которая связывает комментарии с вопросом или ответом (так что комментарии будут иметь идентификатор и комментарий, в таблице соединений будут ID CommentID, AnswerID и QuestionID). Или вы могли бы иметь только таблицу комментариев, а затем иметь таблицу ассоциации "Комментарий к комментарию" и отдельную таблицу ассоциаций вопросов и комментариев.

Ответ 6

Есть два способа, о которых я могу думать.

Сначала используйте другой столбец в таблице комментариев, чтобы указать, принадлежит ли комментарий к Вопросу или Ответу. Таким образом, ПК таблицы комментариев становится wher PostID - это внешний ключ для вопроса или ответа, а PostType может быть примерно 1 = Вопрос и 2 = Ответ.

Во-вторых, используйте таблицу отношений для каждого вопроса и ответа. Таким образом, у вас есть таблица вопросов, ответов, комментариев, вопросов и комментариев.

Предположим, что первичным ключом таблиц "Вопрос", "Ответ", "Комментарий" является "QuestionID", "ReplyID" и "CommentID" соответственно. Тогда столбцы QuestionComment будут [QuestionID, CommentID]. Аналогично, столбцы AnswerComment будут [answerID, CommentID].