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
  );
È stato utile?

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 filosofia

Rails 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].

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top