Domanda

Sto implementando un sistema di tagging per un sito web.Sono presenti più tag per oggetto e più oggetti per tag.Ciò si ottiene mantenendo una tabella con due valori per record, uno per gli ID dell'oggetto e il tag.

Sto cercando di scrivere una query per trovare gli oggetti che corrispondono a un determinato insieme di tag.Supponiamo di avere i seguenti dati (nel formato [oggetto] -> [tag]*)

apple -> fruit red food
banana -> fruit yellow food
cheese -> yellow food
firetruck -> vehicle red

Se voglio abbinare (rosso), dovrei prendere la mela e il camion dei pompieri.Se voglio abbinare (frutta, cibo) dovrei prendere (mela, banana).

Come scrivo una query SQL per fare quello che voglio?

@Jeremy Ruten,

Grazie per la tua risposta.La notazione utilizzata è stata utilizzata per fornire alcuni dati di esempio: il mio database ha una tabella con 1 ID oggetto e 1 tag per record.

In secondo luogo, il mio problema è che devo ottenere tutti gli oggetti che corrispondono a tutti i tag.Sostituendo il tuo OR con un AND in questo modo:

SELECT object WHERE tag = 'fruit' AND tag = 'food';

Non produce risultati durante l'esecuzione.

È stato utile?

Soluzione

Dato:

  • tabella degli oggetti (ID chiave primaria)
  • tabella objecttags (chiavi esterne objectId, tagid)
  • tabella dei tag (ID chiave primaria)

    SELECT distinct o.*
      from object o join objecttags ot on o.Id = ot.objectid
                    join tags t on ot.tagid = t.id
     where t.Name = 'fruit' or t.name = 'food';
    

Questo sembra al contrario, dal momento che vuoi e, ma il problema è che 2 tag non sono sulla stessa riga e quindi an e non produce nulla, poiché 1 singola riga non può essere sia un frutto che un alimento.Questa query produrrà solitamente duplicati, perché otterrai 1 riga di ciascun oggetto, per tag.

Se desideri davvero fare un e in questo caso, avrai bisogno di a group by, e a having count = <number of ors> nella tua query, ad esempio.

  SELECT distinct o.name, count(*) as count
    from object o join objecttags ot on o.Id = ot.objectid
                  join tags t on ot.tagid = t.id
   where t.Name = 'fruit' or t.name = 'food'
group by o.name
  having count = 2;

Altri suggerimenti

Oh Dio, potrei aver interpretato male il tuo commento originale.

Il modo più semplice per farlo in SQL sarebbe avere tre tabelle:

1) Tags ( tag_id, name )
2) Objects (whatever that is)
3) Object_Tag( tag_id, object_id )

Quindi puoi porre praticamente qualsiasi domanda tu voglia sui dati in modo rapido, semplice ed efficiente (a condizione di indicizzarli in modo appropriato).Se vuoi essere fantasioso, puoi anche consentire tag con più parole (c'è un modo elegante e un modo meno elegante, mi viene in mente).

Presumo che sia quello che hai, quindi questo SQL qui sotto funzionerà:

Il modo letterale:

    SELECT obj 
      FROM object
     WHERE EXISTS( SELECT * 
                     FROM tags 
                    WHERE tag = 'fruit' 
                      AND oid = object_id ) 
       AND EXISTS( SELECT * 
                     FROM tags 
                    WHERE tag = 'Apple'
                      AND oid = object_id )

Ci sono anche altri modi per farlo, come ad esempio:

SELECT oid
  FROM tags
 WHERE tag = 'Apple'
INTERSECT
SELECT oid
  FROM tags
 WHERE tag = 'Fruit'

@Kyle:La tua query dovrebbe essere più simile a:

SELECT object WHERE tag IN ('fruit', 'food');

La tua query cercava righe in cui il tag era sia frutta che cibo, il che è impossibile visto che il campo può avere un solo valore, non entrambi contemporaneamente.

Combina il suggerimento di Steve M. con quello di Jeremy otterrai un unico disco con quello che cerchi:

select object
from tblTags
where tag = @firstMatch
and (
       @secondMatch is null 
       or 
       (object in (select object from tblTags where tag = @secondMatch)
     )

Ora, questo non si adatta molto bene, ma otterrà ciò che stai cercando.Penso che ci sia un modo migliore per farlo in modo da poter facilmente avere N numero di elementi corrispondenti senza un grande impatto sul codice, ma al momento mi sfugge.

Ti consiglio il seguente schema.

Objects: objectID, objectName
Tags: tagID, tagName
ObjectTag: objectID,tagID

Con la seguente interrogazione.

select distinct
    objectName
from
    ObjectTab ot
    join object o
        on o.objectID = ot.objectID
    join tabs t
        on t.tagID = ot.tagID
where
    tagName in ('red','fruit')

Suggerirei di fare in modo che la tua tabella abbia 1 tag per record, in questo modo:

 apple -> fruit
 apple -> red
 apple -> food
 banana -> fruit
 banana -> yellow
 banana -> food

Allora potresti semplicemente

 SELECT object WHERE tag = 'fruit' OR tag = 'food';

Se vuoi davvero farlo a modo tuo, potresti farlo in questo modo:

 SELECT object WHERE tag LIKE 'red' OR tag LIKE '% red' OR tag LIKE 'red %' OR tag LIKE '% red %';
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top