2010-05-27 5 views
7

Ho una tabella che memorizza i commenti, il commento può provenire da un altro utente o un altro profilo che sono entità separate in questa app.Migliori pratiche del database

Il mio pensiero originale era che la tabella avrebbe entrambi i campi user_id e profile_id, quindi se un utente invia un commento, dà l'user_id lascia il profile_id vuoto

è questo giusto, sbagliato, c'è un modo migliore ?

risposta

1

In passato ho utilizzato una tabella di commenti centralizzata e aveva un campo per il fk_table a cui fa riferimento.

esempio:

comments(id,fk_id,fk_table,comment_text) 

In questo modo è possibile utilizzare le query UNION per concatenare i dati da diverse fonti.

SELECT c.comment_text FROM comment c JOIN user u ON u.id=c.fk_id WHERE c.fk_table="user" 
UNION ALL 
SELECT c.comment_text FROM comment c JOIN profile p ON p.id=c.fk_id WHERE c.fk_table="profile" 

Ciò garantisce che è possibile espandere il numero di oggetti con commenti senza creare tabelle ridondanti.

4

Se i commenti sono generali per diversi oggetti che si potrebbe creare una tabella per ogni oggetto:

user_comments (user_id, comment_id) 
profile_comments (profile_id, comment_id) 

Allora non c'è bisogno di avere le colonne vuote nella tabella commenti. Sarà anche facile aggiungere nuovi oggetti di origine dei commenti in futuro senza toccare la tabella dei commenti.

+0

+ 1 per la tecnica correttamente normalizzata – DancesWithBamboo

+0

Mi piace questa soluzione ma fare un semplice filtraggio dei commenti per tipo di commentatore o commentatore richiederebbe un join. – cherouvim

+0

Effettivamente - ma un join è un piccolo inconveniente, ed è sempre possibile creare una vista o una stored procedure per questo in modo da non dover pensare al join ogni volta che è necessario ottenere quei dati. – becquerel

3

Un altro modo per risolvere è sempre denormalizzare (copiare) il nome del commentatore sul commento e anche memorizzare un riferimento al commentatore tramite un tipo e un campo ID. In questo modo hai una tabella di commenti unificata dove puoi cercare, ordinare e ritagliare rapidamente. Lo svantaggio è che non esiste alcuna reale relazione FK tra un commento e il proprietario.

+0

Dei metodi menzionati finora, preferisco questo. Sì, non esiste una vera relazione con le chiavi straniere; questo sarà sempre un inconveniente, ma non ci sono molte applicazioni web a preoccuparsene! Questo è il modo più estendibile senza creare molte tabelle diverse che memorizzano informazioni simili. –

+0

Non capisco. stai dicendo, memorizzare una copia del nome dei commentatori ma nessun FK? – Tim

+0

@ user270797: i dati dei commenti sono già sul tavolo. I dati del commentatore (nome, email, ecc.) Devono essere copiati (denormalizzati) sulla tabella dei commenti per un facile accesso. Questa soluzione ha senso in alcuni scenari. – cherouvim

5

Qualunque sia la soluzione migliore dipende IMHO su più di un semplice tavolo, ma anche come questo viene utilizzato altrove nell'applicazione.

Supponendo che i commenti siano tutti associati ad altri oggetti, diciamo che estrai tutti i commenti da quell'oggetto. Nella progettazione proposta, l'estrazione di tutti i commenti richiede la selezione da una sola tabella, che è efficiente. Ma questo sta estraendo i commenti senza estrarre le informazioni sul poster di ogni commento. Forse non vuoi mostrarlo, o forse sono già memorizzati nella memoria.

E se fosse necessario recuperare le informazioni sul poster durante il recupero dei commenti? Quindi devi unirti a due diverse tabelle, e ora il set di record risultante viene inquinato con molti valori NULL (per un commento del profilo, tutti i campi utente saranno NULL). Il codice che deve analizzare questo set di risultati potrebbe anche diventare più complesso.

Personalmente, avrei probabilmente iniziare con la versione completamente normalizzata, e poi denormalizzare quando inizio a vedere i problemi di prestazioni

C'è anche una soluzione completamente diversa possibile il problema, ma questo dipende dal fatto che o non rende senso nel dominio. Cosa succede se ci sono altri posti nell'applicazione in cui un utente e un poster possono essere usati in modo intercambiabile? Cosa succede se un utente è solo un tipo speciale di un profilo? Quindi penso che la soluzione dovrebbe essere risolta generalmente nelle tabelle utente/profilo.Per esempio (alcuni abbreviato pseudo-SQL):

create table AbstractProfile (ID primary key, type) -- type can be 'user' or 'profile' 
create table User(ProfileID primary key references AbstractProfile , ...) 
create table Profile(ProfileID primary key references AbstractProfile , ...) 

allora qualsiasi posto nella propria applicazione, in cui un utente o un profilo possono essere usati in modo intercambiabile, è possibile fare riferimento al LoginID.

+1

+1 Questa è la soluzione che stavo iniziando a scrivere. Vedi anche * Associazioni polimorfiche * in http://www.slideshare.net/billkarwin/practical-object-orientented-models-in-sql –

0

Ecco un altro approccio, che consente di mantenere l'integrità referenziale attraverso chiavi esterne, gestione centralizzata, e di fornire il massimo delle prestazioni utilizzando strumenti di database standard come indici e se si ha realmente bisogno, partizionamento, ecc:

create table actor_master_table(
    type char(1) not null, /* e.g. 'u' or 'p' for user/profile */ 
    id varchar(20) not null, /* e.g. 'someuser' or 'someprofile' */ 
    primary key(type, id) 
); 

create table user(
    type char(1) not null, 
    id varchar(20) not null, 
    ... 
    check (id = 'u'), 
    foreign key (type, id) references actor_master_table(type, id) 
); 

create table profile(
    type char(1) not null, 
    id varchar(20) not null, 
    ... 
    check (id = 'p'), 
    foreign key (type, id) references actor_master_table(type, id) 
); 

create table comment(
    creator_type char(1) not null, 
    creator_id varchar(20) not null, 
    comment text not null, 
    foreign key(creator_type, creator_id) references actor_master_table(type, id) 
);