2014-10-15 14 views
6

Ho un problema con la progettazione di database (SQL/MySQL). Supponiamo di avere un utente , l'utente può avere molti amici e molti post e riempire alcuni dati su se stesso.Progettazione database - relazioni vs proprietà

E 'abbastanza ovvio che per friends abbiamo bisogno di un tabella_pivot per n: n relazione, per posts abbiamo bisogno di creare una tabella in più con user_id (1: n) relazione.

Quindi abbiamo bisogno delle tabelle users, user_friends e posts. Questo è ovvio. Ecco come devono essere gestite le relazioni.

Ma ora supponiamo che vogliamo per gli utenti di avere i seguenti dati:

name - text 
description - text 
marital status - select only one from list 
favourite colour - select only one from list 
hobby - select up to 3 from list 

Per i campi di testo (nome, descrizione) è davvero ovvio che semplicemente creare colonne varchar/testo in users tavolo e il gioco è fatto.

La domanda generale è: come devono essere gestiti gli altri campi (scelti dagli elenchi)? Devo creare delle relazioni per loro o forse dovrei creare colonne di dati standard con loro?

Secondo me non ha senso creare tabelle di relazione per questo perché usando le liste (seleziona) limitiamo l'utente solo quando può infatti incollarlo nel database. In teoria potremmo consentire all'utente di inserire manualmente come colore preferito il suo colore (ad esempio red e se digita qualcosa di sbagliato, ad esempio reds, confronteremo l'elenco con colours consentito). Lo stesso sarebbe per il genere - non c'è motivo, secondo me, di creare un tavolo in più quando teniamo solo la donna e l'uomo e creiamo una relazione per esso. disegno

Prima DB:

potrei ad esempio creare le seguenti colonne per le proprietà:

marital_status - int 
fav_colour - int 
hobby_1 - int 
hobby_2 - int 
hobby_3 - int 

E avere un altro tavolo (o anche di matrice normale in PHP o altra lingua) dove ho negozio che il valore 1 per fav_colour è ad esempio rosso, valore 2 per hobby è musica e così via (non importa come li memorizzi qui - potrei anche usare il tipo enum per quello).

Per me i vantaggi di tale atteggiamento non creano molte relazioni che sono in realtà proprietà e non relazioni (come ho detto sopra), quindi meno lavoro + più facile ottenere informazioni sull'utente - non è necessario utilizzare alcun join cosa sarebbe importante se si ha per l'utente per esempio 20 o 100 tali proprietà e posso cercare nella tabella utente molto facile. Gli svantaggi sono anche abbastanza ovvi: i dati non sono normalizzati, per qualsiasi selezione multipla (come per esempio l'hobby) ho bisogno di creare 3 colonne e se in futuro deciderò che l'utente può selezionare non 1 colore ma 2 o 3, dovrei aggiungere 2 colonne extra.

Alternativa disegno DB:

creo tavoli extra: colours, hobbies, marital_statuses e creo 3 perni tabelle: user_colours, user_hobbies, user_marital_statuses. Svantaggi: molti si uniscono. Vantaggi: se avessi creato 3 tabelle pivot aggiuntive, avrei potuto facilmente consentire all'utente di selezionare fino a 10 colori e non ho affatto bisogno di un database di riprogettazione. Ma si verificano anche degli svantaggi: ricerca difficile, molto lavoro, molti join.

domanda dettagliata

Quindi, per riassumere - quale soluzione sarebbe meglio assumendo:

  1. io probabilmente non cambiare il numero massimo di uno proprietà (se ho deciso permetto massimo 3 hobby, questo probabilmente non cambierà mai)
  2. Le liste di scelta per molti campi sarebbero relativamente brevi (per la maggior parte di esse meno di 10)
  3. Ho bisogno di cercare molto in tale database. Qualcuno, ad esempio, desidera cercare utenti che hanno impostato il colore fav_color al rosso e hanno musica per hobby.

Se ci sono altre soluzioni o vantaggi/svantaggi si vede che apprezzo condividere con me.

+1

Ecco un'altra opzione. Creare una tabella di attributi con attributeName, attributeType, attributeValue, userId. Ciò ti consentirà di aggiungere tutti gli attributi a un utente come desideri. Evitare di dover apportare modifiche allo schema di interruzione ogni volta che si pensa a un nuovo bit di informazioni che si desidera. – paqogomez

+4

@paqogomez: che è un anti-modello chiamato "valore-attributo entità" ma probabilmente la soluzione che fa meno male qui. Un'altra opzione sarebbe quella di memorizzare gli "attributi dinamici" come un documento JSON o XML. Ma questo rende molto difficile gestirli in SQL. Una terza opzione potrebbe essere l'aggiornamento a Postgres e l'uso delle funzionalità NoSQL di Postgres come il tipo di dati chiave/valore 'hstore' o il supporto JSON incorporato –

+0

@a_horse_with_no_name In realtà avevo un commento che suggeriva un'opzione NoSQL, ma l'ho rimosso . Ho pensato che non era la direzione che l'OP voleva andare. È esattamente questo tipo di dati per cui NoSQL è stato creato. – paqogomez

risposta

1

Sembra che si voglia applicare alcuni vincoli su alcune proprietà degli utenti. Ad esempio, il colore preferito deve essere uno di rosso, verde, blu, rosa, arancione, ecc; lo stato civile deve essere uno dei single, divorziato, sposato.

Hai descritto un modo per farlo: tabelle di ricerca. Questo è il modo migliore se i valori possibili sono dinamici e richiedono una manutenzione continua o se ci sono molti valori possibili. Dalla tua descrizione, questa non è la tua situazione. I tuoi valori possibili saranno abbastanza statici e brevi.

Si consiglia di utilizzare un vincolo sql CHECK. Con esso, puoi controllare i possibili valori di un campo. Per esempio:

CREATE TABLE users 
(
Name varchar(255) NOT NULL, 
Description varchar(255), 
Marital_Status varchar(10) NOT NULL, 
Color varchar(10) NOT NULL, 
CONSTRAINT chk_Color CHECK (Color in ('Red', 'Blue', 'Green', 'Orange')), 
CONSTRAINT chk_Marriage CHECK (Marital_Status in ('Single', 'Married', 'Divorced')) 
) 

non ho Sintassi verificata questa dichiarazione DDL, quindi potrebbe contenere errori di punteggiatura. Inoltre, la sintassi può variare a seconda del tuo particolare DBMS. Penso che questo dovrebbe funzionare per MySQL.

+0

Potrebbe andar bene, ma per molti colori non sarebbe molto facile inserirli in Constraint (e aggiungerne un altro se necessario). Anche la memorizzazione di varchars avrebbe un grosso problema. Se il sito è multilingue, la memorizzazione di varchar non sarà una buona idea qui penso. –

1

Se gli utenti possono modificare frequentemente i colori/gli hobby preferiti, utilizzerei le tabelle lookup, nel mio esempio mi riferirò a loro come tabelle decode. Tutte le relazioni tra user/hobbies e user/colors si trovano nella tabella decode.

Dal momento che è possibile avere solo 1 marital status, è facile gestire la relazione da 1 a molti.

Creare una tabella Marital_Status con 2 campi, Id (pk) e Status(varchar(n)) La tabella decode non sarà richiesto per ricerca marital status.

Ora suggerirei di creare una tabella per contenere colors e una tabella per hobbies. Allo stesso modo abbiamo fatto marital status.

Hobbies 

HobbyId, Hobby 

Colors 
ColorId, Color 

Ogni volta che è necessario aggiungere/eliminare una nuova hobby/color farlo in queste decode tabelle.

Sta a te decidere se vuoi utilizzare la tabella 1 decode per ogni relazione o più. Hobby_Decode and Color_Decode ecc

Mi spiego lo scenario di utilizzo 1.

creare la tabella di decodifica con i seguenti campi ...

Decode

Item_Type varchar(n) --Non spingerà sia Hobby o Color in questo campo

UserId int --self esplicativo, detiene la ID dell'utente per "ricerca"

LookupId - conserverà gli ID di Hobby o Color

Consentitemi di creare alcuni dati di esempio e lavoreremo su questo.

Hobbies table dati

| HobbyId | Hobby 

     1  Studying 
     2  Doing Drugs 
     3  Drinking  

Colors table dati

| ColorId | Color 

    1  Red 
    2  Blue 

Già che ci siamo, ecco la nostra tabella degli utenti.

Users

| UserId | Name 

     1  Marcin 
     2  CSharper 

Mi piace bere, fare la droga e il colore rosso, Sei un nerd in modo che ti piace di studiare e il colore blu. Nella nostra tabella di decodifica aggiungeremo le seguenti voci per rappresentarlo.

Decode

| Item_Type| UserId | LookUpId 

    'Hobby'  2  2 
    'Hobby'  2  3 
    'Color'  2  1 
    'Hobby'  1  1 
    'Color'  1  2  

Guardando a quel tavolo decodifica in realtà non ci dice nulla. Una volta che ci uniremo al nostro tavolo decode a colors/hobbies sarà evidente.

Se si desidera cercare tutti i miei hobby e miei colori preferiti la query sarà presente nota

: questa è la sintassi SQL Server non MySQL.

--Pull Hobbies 
Select u.Name, dH.Item_Type as 'Favorite', h.Hobby as 'Item' 
from User u 
inner join decode dH on dH.UserId = u.UserId 
        and dH.Item_Type = 'Hobby' 
inner join Hobby h on h.HobbyId = dH.LookUpId 
where u.UserId = 2 

--Union in Colors 
Union 

Select u.Name, dH.Item_Type as 'Favorite', h.Hobby 'Item' 
from User u 
inner join decode dC on dH.UserId = u.UserId 
        and dH.Item_Type = 'Color' 
inner join Color c on c.ColorId = dH.LookUpId 
where u.UserId = 2 

L'output sarà simile

| Name | Favorite |  Item 

    CSharper   Hobby   Drinking 
    CSharper   Hobby   Doing Drugs 
    CSharper   Color   Red 

Se è configurato in questo modo, di quello che è estremamente facile da cambiare i popoli/aggiornamento hobby e colori preferiti. La tabella decode gestirà tutto ciò. Basta una semplice immissione o cancellazione di quella tabella. E anche in questo modo, un utente può avere una quantità infinita di hobby e colori preferiti dal momento che è la tabella di decodifica che lo guida, e non la definizione della tabella Users.

Manipolare un po 'la vostra query di esempio, se vogliamo trovare tutti gli utenti a cui piace il colore blu e bere la query sarebbe simile.

Select u.Name 
from User u 
inner join decode d on d.UserId = u.UserId 
inner join Hobby h on h.HobbyId = d.LookUpId and d.Item_Type = 'Hobby' 
inner join Color c on C.ColorId = d.LookUpId and d.Item_Type = 'Color' 
where h.Hobby = 'drinking' and c.Color = 'blue' 

Fare join così è perfettamente accettabile.

+0

Hmm, non sarebbe complesso? Cosa succede se voglio ottenere tutte le proprietà per un utente. Qui abbiamo colori e fino a 3 hobby. Ma cosa succede se ci sono più di 20 "relazioni"? Non sarebbe troppo complesso e la velocità d'impatto? –

+0

No per niente, lavoro in una società finanziaria e mi fido di me, le query sono pesanti per non dire altro e questo funziona bene. Puoi lanciare un indice su UserId e Item_type per accelerare ulteriormente le cose. Puoi dividerlo e utilizzare più tabelle di decodifica se lo desideri, probabilmente sarebbe più facile e più leggibile nel tuo caso. Se dichiari che un utente può avere quantità variabili di Preferito X, non conosco una soluzione più semplice. Cambiare costantemente la tabella degli utenti per incorporare più X preferiti non sarebbe ottimale. Una tabella di decodifica per mantenere ogni relazione funzionerebbe. – CSharper

1

Si desidera evitare tabelle e join aggiuntivi a meno che non sia realmente necessario. Questo è esattamente ciò a cui servono le enumerazioni.le enumine vengono memorizzate internamente come numeri interi e in uso sembrano stringhe con valori vincolati.

create table users (
    user_id bigint unsigned not null auto_increment primary key, 
    name varchar(255) not null, 
    description varchar(255), 
    marital_status enum('single', 'married'), 
    favorite_color enum('red', 'green', 'blue'), 
    hobby1 enum('painter', 'doctor', 'lawyer'), 
    hobby2 enum('painter', 'doctor', 'lawyer'), 
    hobby3 enum('painter', 'doctor', 'lawyer') 
); 

Per inserire un valore: insert into table users (name, marital_status) values ('Jack', 'single');

Questa dichiarazione avrà esito negativo: insert into table users (name, marital_status) values ('Jack', 'abcd');

Modifica della lista è un'operazione semplice e veloce: alter table users modify marital_status enum('divorced', 'single', 'married');

+0

Ok, ma per quanto riguarda la convalida dei dati? Come posso verificare quali valori sono consentiti? Ad esempio, l'utente riempirà di colore abc. Posso interrogare il campo enum per ottenere valori consentiti? E la domanda in più - come conservare le traduzioni per loro? Supponendo che usiamo più di 1 lingua sulla pagina, i nomi dovrebbero essere duplicati in altre tabelle/dati per memorizzare la traduzione –

+0

Puoi usare "show create table 'tablename'" o interrogare il [information_schema] (http: // stackoverflow. it/questions/2350052/how-can-i-get-enum-possibili-valori-in-a-mysql-database) per ottenere i valori consentiti in un enum. Per le traduzioni, si desidera avere valori indipendenti dalla lingua che si utilizzano nell'enumerazione e le traduzioni che saranno una tabella separata. –

0

Qualunque sia si sceglie è buono, don fare molto affidamento sulla normalizzazione.

Ma per me, sarebbe andato con 5 tavoli users, marital_status, colours, hobbies, user_hobbies

CREATE TABLE users (
    user_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) NOT NULL, 
    description VARCHAR(255), 
    marital_status INT, 
    fav_colour INT 
) 

CREATE TABLE marital_status (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) NOT NULL 
) 

CREATE TABLE colours (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) NOT NULL, 
    code VARCHAR(7) 
) 

CREATE TABLE hobbies (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) NOT NULL 
) 

CREATE TABLE user_hobbies (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    user_id BIGINT, 
    hobby_id INT 
) 

Per le tabelle pivot, vorrei suggerire di creare/li popolano separatamente dalle applicazioni, come ad esempio utilizzando il comando linea o coda messaggi (utilizzando la funzionalità crontab)

Problemi correlati