Question

J'implémente un système de balisage pour un site Web.Il existe plusieurs balises par objet et plusieurs objets par balise.Ceci est accompli en conservant une table avec deux valeurs par enregistrement, une pour les identifiants de l'objet et la balise.

Je cherche à écrire une requête pour trouver les objets qui correspondent à un ensemble de balises donné.Supposons que j'aie les données suivantes (au format [object] -> [tags]*)

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

Si je veux faire correspondre (rouge), je devrais acheter une pomme et un camion de pompier.Si je veux assortir (fruits, nourriture), je devrais obtenir (pomme, banane).

Comment écrire une requête SQL pour faire ce que je veux ?

@Jérémy Ruten,

Merci pour votre réponse.La notation utilisée a été utilisée pour donner des exemples de données - ma base de données a une table avec 1 identifiant d'objet et 1 balise par enregistrement.

Deuxièmement, mon problème est que j'ai besoin d'obtenir tous les objets correspondant à toutes les balises.En remplaçant votre OR par un AND comme ceci :

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

Ne donne aucun résultat lors de l'exécution.

Était-ce utile?

La solution

Donné:

  • table d'objets (identifiant de clé primaire)
  • table objecttags (clés étrangères objectId, tagid)
  • table de balises (identifiant de clé primaire)

    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';
    

Cela semble à l'envers, puisque vous voulez and, mais le problème est que 2 balises ne sont pas sur la même ligne, et par conséquent, an and ne donne rien, puisqu'une seule ligne ne peut pas être à la fois un fruit et un aliment.Cette requête produira généralement des doublons, car vous obtiendrez 1 ligne de chaque objet, par balise.

Si vous souhaitez vraiment faire un et dans ce cas, vous aurez besoin d'un group by, et un having count = <number of ors> dans votre requête par exemple.

  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;

Autres conseils

Oh mon Dieu, j'ai peut-être mal interprété votre commentaire original.

La manière la plus simple de procéder en SQL serait d'avoir trois tables :

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

Vous pouvez ensuite poser pratiquement toutes les questions que vous souhaitez sur les données rapidement, facilement et efficacement (à condition de les indexer de manière appropriée).Si vous voulez faire preuve de fantaisie, vous pouvez également autoriser les balises multi-mots (il existe une manière élégante et une manière moins élégante à laquelle je peux penser).

Je suppose que c'est ce que vous avez, donc ce SQL ci-dessous fonctionnera :

La manière littérale :

    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 )

Il existe également d'autres façons de le faire, telles que :

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

@Kyle :Votre requête devrait ressembler davantage à :

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

Votre requête recherchait des lignes où la balise était à la fois un fruit ET un aliment, ce qui est impossible étant donné que le champ ne peut avoir qu'une seule valeur, pas les deux en même temps.

Combinez la suggestion de Steve M. avec celle de Jeremy, vous obtiendrez un seul disque avec ce que vous recherchez :

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

Maintenant, cela ne s’adapte pas très bien, mais cela obtiendra ce que vous recherchez.Je pense qu'il existe une meilleure façon de procéder afin que vous puissiez facilement avoir un nombre N d'éléments correspondants sans beaucoup d'impact sur le code, mais cela m'échappe actuellement.

Je recommande le schéma suivant.

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

Avec la requête suivante.

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')

Je suggère de faire en sorte que votre table ait 1 balise par enregistrement, comme ceci :

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

Alors tu pourrais juste

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

Si vous voulez vraiment procéder à votre manière, vous pouvez le faire comme ceci :

 SELECT object WHERE tag LIKE 'red' OR tag LIKE '% red' OR tag LIKE 'red %' OR tag LIKE '% red %';
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top