In un clone StackOverflow, che rapporto dovrebbe un tavolo Commenti dover domande e risposte?
-
13-09-2019 - |
Domanda
In un'applicazione simile a StackOverflow che sto costruendo, sto cercando di decidere quale rapporto miei Questions
, tavoli Answers
e Comments
dovrebbero avere.
ho potuto avere Questions
e Answers
sia essere rappresentato da un unico tavolo Posts
.
Ciò consentirebbe Comments
di avere una sola chiave estranea alla Posts
.
Ma se Questions
e Answers
sono tabelle separate, quali relazioni dovrebbe Comments
deve ognuno di questi?
UPDATE: Anche se la risposta scelta raccomanda un approccio Ereditarietà Classe Tavolo e questo mi sembra la soluzione migliore in termini di database, questa opzione non è supportata dal Rails ORM. Così, in Rails miei modelli dovranno utilizzare Single Table Inheritance e probabilmente simile a questa:
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
);
Soluzione
mi piacerebbe andare con l'approccio messaggi. Questo è il modo migliore per garantire l'integrità referenziale.
Se avete bisogno di, rispettivamente, colonne aggiuntive per le risposte e domande, metterli in tabelle aggiuntive con una relazione uno-a-uno con messaggi.
Per esempio, nella sintassi 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;
Questa è chiamata classe Ereditarietà delle tabelle. C'è una bella panoramica della modellazione eredità con SQL in questo articolo: " eredità nel database relazionali ".
Può essere utile utilizzare post_type così un dato messaggio può essere solo una risposta o una domanda. Tu non vuoi sia una risposta e una domanda per fare riferimento a un determinato posto. Quindi questo è lo scopo della colonna post_type
sopra. È possibile utilizzare vincoli CHECK per far rispettare i valori in post_type
, oppure utilizzare un trigger se il database non supporta i vincoli CHECK.
Ho anche fatto una presentazione che può aiutare. Gli scivoli sono fino a http://www.slideshare.net/billkarwin/sql- antipattern-strike-back. Si consiglia di leggere le sezioni polimorfici Associazioni e Entità-attributo-valore.
Se si utilizza Single Table Inheritance, come hai detto che si sta utilizzando Ruby on Rails, allora il DDL SQL sarebbe simile a questa:
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;
È possibile utilizzare un vincolo di chiave esterna in questo esempio, e vi consiglio di fare! : -)
La filosofiaRails tende a favorire mettere l'applicazione del modello di dati nel livello di applicazione. Ma senza vincoli di far rispettare l'integrità a nel database, si ha il rischio che i bug nell'applicazione, o query ad hoc da uno strumento di query, possono danneggiare l'integrità dei dati.
Altri suggerimenti
Nelle reti sociali che ho costruito io faccio qualcosa di un po 'diverso. Se ci pensate un commento potrebbe essere collegato a quasi tutte le entità in un sito. Questo potrebbe essere un post sul blog, un forum di discussione o inviati, un articolo, di qualcuno immagine, una persone profilo, un fornitore di un servizio, ecc Per questo motivo ho creato una tabella SystemObjects che contiene il tipo di oggetto (di riferimento tabella). Per la maggior parte creo record per le Entità del mio sistema che accetteranno i commenti ... ma questi traccio direttamente ai miei tavoli. La tabella SystemObjects ospita la SystemObjectID, e un nome descrittivo per riferimento futuro (una tabella di ricerca).
Con questo posto ho quindi creare una tabella di commenti che ha il riferimento SystemObjectID di dirmi cosa tavolo per andare a cercare. Poi ho anche la casa dei SystemObjectRecordID che mi dice che PK della tabella di riferimento Sono interessato a (insieme a tutti i commenti di dati standard).
Io uso questa nozione della tabella systemobject per molti altri concetti generici di vasta portata nei miei siti. Pensate a Tag, Valutazioni, commenti e qualsiasi altro frutto penzoloni che potrebbe essere collegato attraverso il vostro sito e aggregati per l'utilizzo rapido.
Per saperne di più su questo nel mio libro ASP.NET 3.5 Social Networking .
Si potrebbe creare una singola tabella commenti con due chiavi esterne, una per questions.questionID e l'altro per answers.answerId
Si avrebbe bisogno di due tabelle di dominio che portano i rapporti insieme CommentsForQuestions e CommentsForAnswers. In sostanza si sta andando ad avere bisogno di creare 5 tabelle per questo scopo:
Questions
Answers
Comments
CommentsForQuestions (relates comments to questions)
CommentsForAnswers (relates comments to answers)
L'unico problema questo ha è che è inferiore al concetto messaggi perché l'integrità referenziale non è così forte. Posso garuntee che CommentsForQuestions si connette a un commento e una domanda, ma non posso impedire che sia una domanda e una risposta di connettersi al lo stesso commento.
Una relazione di chiave esterna; è possibile avere QuestionComments e AnswerComments, o si può avere Commenti hanno una colonna di chiave esterna per entrambe le domande e risposte (e avere quelle colonne in esclusiva).
Personalmente, mi piacerebbe andare con l'approccio messaggi.
Modifica: Su considerazione, c'è un terzo approccio che potrebbe funzionare; si potrebbe avere un tavolo commenti, e poi basta avere una tabella di associazione che associa i commenti sia con una domanda o una risposta (così Commenti avrebbe un ID e il commento, la tabella unirsi avrebbe un CommentID, un answerID, e un QuestionID ). Oppure, si potrebbe avere solo un tavolo commenti, poi hanno una tabella di associazione risposta-commento, e una tabella di associazione domanda-commento a parte.
Ci sono 2 modi che mi viene in mente.
In primo luogo, utilizzare un'altra colonna della tabella Comment per indicare se il commento appartiene alla domanda o risposta. Così il PK della tabella Commento diventa wher PostID è la chiave esterna alla domanda o una risposta e PostType può essere someting come 1 = Domanda e 2 = risposta.
In secondo luogo, utilizzare una tabella di relazioni per ogni domanda e risposta. In modo da avere una domanda, risposta, commento, QuestionComment, e tavolo AnswerComment.
Diciamo che la chiave primaria della domanda, risposta, tavoli di commento sono QuestionID, answerID e CommentID rispettivamente. Poi le colonne di QuestionComment sarebbero [QuestionID, CommentID]. Similmente, le colonne di AnswerComment sarebbero [answerID, CommentID].