2010-05-04 11 views
6

In questo momento ho intenzione di aggiungere un sistema di filtri al mio sito.Come implementare il sistema di filtri in SQL?

Esempi:

(ID=apple, COLOR=red, TASTE=sweet, ORIGIN=US) 
(ID=mango, COLOR=yellow, TASTE=sweet, ORIGIN=MEXICO) 
(ID=banana, COLOR=yellow, TASTE=bitter-sweet, ORIGIN=US) 

così ora io sono interessato a fare il seguente: SELECT MATR FROM thisTable dove il colore = 'giallo' E GUSTO = 'DOLCE'

Ma il mio problema è che io sono facendo questo per più categorie nel mio sito, e le colonne NON sono coerenti. (come se il tavolo fosse per handphone, allora sarà BRAND, 3G-ENABLED, PREZZO, COLOR, WAVELENGTH, ecc.)

come potrei progettare uno schema generale che permetta questo?

In questo momento sto progettando di fare:

table(ID, KEY, VALUE) 

Questo permette il numero arbitrario di colonne, ma per la query, io sto usando SELECT MATR FROM tabella WHERE (KEY = X1 E VALORE = V1) E (KEY = X2 AND VALUE = V2), .. che restituisce un set vuoto.

Qualcuno può consigliare una buona soluzione a questo? Si noti che il numero di colonne cambierà regolarmente

+1

è con sorpresa che Reddit utilizza EVA ampiamente. http://carsonified.com/blog/dev/steve-huffman-on-lessons-learned-at-reddit// – crapbag

risposta

0

Quello che si suggerisce è noto come una struttura di Entity-Attribute-Value ed è altamente sconsigliato. Uno dei (molti) grandi problemi con i progetti EAV, ad esempio, è l'integrità dei dati. In che modo imponi che i colori consistano solo in "rosso", "giallo", "blu" ecc.? In breve, non è possibile senza un sacco di hack. Un altro problema si impenna nelle query (come avete visto) e nella ricerca di dati.

Invece, suggerirei di creare una tabella che rappresenti ciascun tipo di entità e quindi ogni tabella possa avere attributi (colonne) specifici per quel tipo di entità.

Per convertire i dati in colonne in una query di risultato come si sta cercando, sarà necessario creare ciò che viene spesso chiamato una query a campi incrociati. Ci sono motori di report che lo faranno e si può fare il codice ma la maggior parte dei prodotti di database non lo farà in modo nativo (cioè senza costruire manualmente la stringa SQL). Naturalmente, le prestazioni non saranno buone se si hanno molti dati e si verificheranno problemi di filtraggio dei dati. Ad esempio, supponiamo che alcuni dei valori siano numerici. Poiché è probabile che la parte value dell'EAV sia una stringa, sarà necessario eseguire il cast di quei valori su un numero intero prima di poterli filtrare e presumere che i dati saranno convertibili in un numero intero.

7

Il modello entity-attribute-value che suggerisci potrebbe rientrare in questo scenario.

Per quanto riguarda la query di filtro, è necessario comprendere che con il modello EAV si sacrifica molta potenza di interrogazione, quindi questo può diventare piuttosto complicato. Tuttavia, questo un modo per affrontare il problema:

SELECT stuff.id 
FROM  stuff 
JOIN  (SELECT COUNT(*) matches 
      FROM  table 
      WHERE  (`key` = X1 AND `value` = V1) OR 
        (`key` = X2 AND `value` = V2) 
      GROUP BY id 
     ) sub_t ON (sub_t.matches = 2 AND sub_t.id = stuff.id) 
GROUP BY stuff.id; 

Una caratteristica poco elegante di questo approccio è che è necessario specificare il numero di coppie/valore di attributo che si prevede di abbinare in sub_t.matches = 2. Se avessimo tre condizioni, avremmo dovuto specificare sub_t.matches = 3 e così via.

Costruiamo un banco di prova:

CREATE TABLE stuff (`id` varchar(20), `key` varchar(20), `value` varchar(20)); 

INSERT INTO stuff VALUES ('apple', 'color', 'red'); 
INSERT INTO stuff VALUES ('mango', 'color', 'yellow'); 
INSERT INTO stuff VALUES ('banana', 'color', 'yellow'); 

INSERT INTO stuff VALUES ('apple', 'taste', 'sweet'); 
INSERT INTO stuff VALUES ('mango', 'taste', 'sweet'); 
INSERT INTO stuff VALUES ('banana', 'taste', 'bitter-sweet'); 

INSERT INTO stuff VALUES ('apple', 'origin', 'US'); 
INSERT INTO stuff VALUES ('mango', 'origin', 'MEXICO'); 
INSERT INTO stuff VALUES ('banana', 'origin', 'US'); 

Query:

SELECT stuff.id 
FROM  stuff 
JOIN  (SELECT COUNT(*) matches, id 
      FROM  stuff 
      WHERE  (`key` = 'color' AND `value` = 'yellow') OR 
        (`key` = 'taste' AND `value` = 'sweet') 
      GROUP BY id 
     ) sub_t ON (sub_t.matches = 2 AND sub_t.id = stuff.id) 
GROUP BY stuff.id; 

Risultato:

+-------+ 
| id | 
+-------+ 
| mango | 
+-------+ 
1 row in set (0.02 sec) 

Ora inseriamo un altro frutto con color=yellow e taste=sweet:

INSERT INTO stuff VALUES ('pear', 'color', 'yellow'); 
INSERT INTO stuff VALUES ('pear', 'taste', 'sweet'); 
INSERT INTO stuff VALUES ('pear', 'origin', 'somewhere'); 

La stessa query restituirebbe:

+-------+ 
| id | 
+-------+ 
| mango | 
| pear | 
+-------+ 
2 rows in set (0.00 sec) 

Se vogliamo limitare questo risultato a entità con origin=MEXICO, avremmo dovuto aggiungere un altro OR condizioni e verificare la presenza di sub_t.matches = 3 invece di 2.

SELECT stuff.id 
FROM  stuff 
JOIN  (SELECT COUNT(*) matches, id 
      FROM  stuff 
      WHERE  (`key` = 'color' AND `value` = 'yellow') OR 
        (`key` = 'taste' AND `value` = 'sweet') OR 
        (`key` = 'origin' AND `value` = 'MEXICO') 
      GROUP BY id 
     ) sub_t ON (sub_t.matches = 3 AND sub_t.id = stuff.id) 
GROUP BY stuff.id; 

Risultato:

+-------+ 
| id | 
+-------+ 
| mango | 
+-------+ 
1 row in set (0.00 sec) 

Come in ogni approccio, ci sono alcuni vantaggi e svantaggi quando si utilizza il modello di EAV. Assicurati di effettuare ricerche approfondite sull'argomento nel contesto della tua applicazione. Potresti anche voler considerare un database relazionale alternativo, come Cassandra, CouchDB, MongoDB, Voldemort, HBase, SimpleDB o altri negozi di valore-chiave.

+0

Wow, sembra molto complicato. Grazie per la soluzione.Ci sono molte persone che si oppongono a me usando questo design, quindi per ora sto seriamente considerando se dovrei usare il modello EVA – crapbag

+0

@sadvaw: L'opposizione deriva principalmente dal fatto che quando si utilizza il modello EAV in un database relazionale, è come usare un furgone per portarti in giro per la città: quindi non lo stai usando per quello che è stato costruito per farlo. Tuttavia può ancora essere fatto, e la fattibilità di tale spesso dipende dalla scala (quanto lo fai, o quanto grande). Quindi direi che se tutto ciò che stai facendo nel database è questo, allora considererei effettivamente le alternative a un RDBMS. Tuttavia se hai un database più grande e questa è solo una piccola parte, allora queste considerazioni potrebbero essere meno importanti. –

0

Il prezzo che si paga per il design semplicistico della tabella in questa fase ti costerà in termini di prestazioni a lungo termine. L'utilizzo di ORM per ridurre i costi di modifica del database per adattarsi ai dati in una struttura appropriata sarebbe probabilmente un buon investimento, anche a dispetto dei costi delle prestazioni dell'ORM.

Altrimenti, è possibile cercare un "ORM inverso" che mappa il codice dal database, che ha il vantaggio di essere meno costoso e con prestazioni più elevate. (Costi iniziali leggermente più elevati rispetto all'ORM, ma migliori prestazioni e affidabilità a lungo termine.)

È un problema costoso indipendentemente da come lo si affetta. Vuoi pagare ora con il tempo di sviluppo o pagare più tardi quando i tuoi carri armati? (. "Paga dopo" è la risposta sbagliata)

+0

Potete consigliare un design del tavolo adatto alla tua risposta? Non capisco davvero a cosa ti riferisci. – crapbag

+0

Mi sono imbattuto nel nome della teoria a cui alludevo: Anchor Modeling. La fonte è un po 'accademica: http://syslab.dsv.su.se/profiles/blogs/anchor-modeling quindi potresti trovare questa spiegazione un po' più facile da digerire: http://askmonty.org/wiki/Manual: Table_Elimination Il punto (correlato ma separato) della conversione del database procedurale (ORM o tecniche ORM inverse) consiste nel ridurre la quantità di codice che si deve scrivere per accedere a una struttura dati più complessa e specializzata con prestazioni, normalizzazione e relazioni superiori. caratteristiche. – cbednarski

1

Di seguito ha lavorato per me:

SELECT * FROM mytable t WHERE 
    t.key = "key" AND t.value = "value" OR 
    t.key = "key" AND t.value = "value" OR 
    .... 
    t.key = "key" AND t.value = "value" 
GROUP BY t.id having count(*)=3; 

count (*) = 3 deve corrispondere alla quantità di

t.key = " tasto" E t.value = "valore"

casi

Problemi correlati