2010-01-04 4 views
6

J'ai une application Web qui met en correspondance les images avec les balises, et je dois créer un moyen d'affiner les résultats de manière dynamique pour la recherche de balises. Cependant, je ne peux pas trouver un moyen propre de faire ces requêtes SQL, et c'est là que j'ai besoin de votre aide.SQL n-à-n correspondant à plusieurs valeurs

L'idée est que si je recherche des balises "clean" et "dog", j'obtiendrai des résultats d'image qui ont à la fois les balises "clean" et "dog". Si j'inclus également le tag "little", mes résultats devront se limiter aux images associées aux trois tags. Donc, ayant une relation N-à-N, quelle est la bonne façon de le faire?

Mon approche naturelle générait quelque chose de code comme ça, mais je ne doute pas comme où il va:

SELECT images.* 
FROM images 
INNER JOIN image_tags ON ... 
INNER JOIN tags ON ... 
WHERE tags.tag = @tag1 
AND EXISTS 
(
    SELECT 1 
    FROM images 
    INNER JOIN image_tags ON ... 
    INNER JOIN tags ON ... 
    WHERE tag = @tag2 
    AND EXISTS 
    (
    SELECT 1 
    FROM images 
    INNER JOIN image_tags ON ... 
    INNER JOIN tags ON ... 
    WHERE tag = @tag3 
    AND EXISTS (...) 
    ... 
) 
) 

Certes, ce n'est pas vraiment bon. Une idée?

Merci!

Répondre

7

Quelque chose comme ça pourrait fonctionner (j'utiliser id pour SELECT et GROUP BY, utilisez les colonnes dont vous avez besoin.

SELECT images.id 
FROM images 
INNER JOIN image_tags ON ... 
INNER JOIN tags ON ... 
WHERE tags.tag IN (@tag1, @tag2, @tag3) 
GROUP BY images.id 
HAVING COUNT(*) = @number_of_tags 

Si vous avez 3 balises comme dans votre exemple alors number_of_tags faudrait être 3, et rejoindre entraînerait 3 lignes par id qui correspond.

Vous pouvez créer cette requête dynamiquement, ou définir avec, disons, 10 balises et les initialiser avec une valeur qui ne se produira pas dans les balises.

+0

Ceci est assez rigide dans le nombre de balises permises/requises, ainsi que renvoyer une ligne pour chaque balise spécifiée, plutôt que pour chaque image. –

+0

Le 'GROUP BY' devrait éviter de renvoyer une ligne pour chaque tag. J'ai édité la question pour montrer comment cela fonctionnerait avec un nombre dynamique de tags. –

+0

Merci beaucoup! Je n'ai pas pensé à revérifier les résultats avec HAVING COUNT(). – Alpha

0

Je n'utiliserais pas une relation N-N mais un champ de texte pour stocker les étiquettes. Cela peut paraître sale car nous perdons la normalité, mais les balises ne sont généralement utilisées que pour la recherche de texte et l'espace disque est bon marché.

Vous pouvez ensuite exécuter

SELECT * FROM images WHERE tags LIKE '%clean%' AND tags LIKE '%dog%'... 
+0

@Peter - Nice alphabétisation ... d vient AFTER c ... ;-) –

+0

Note: Votre solution rendrait le comptage d'images par tag et de renommer ou de supprimer des balises plus compliquées. –

+0

@ md5sum: OMG! Désolé, j'ai dû supprimer ce poste ;-) –

0

En utilisant Intersection, vous pouvez le faire:

SELECT images.* 
FROM images 
WHERE image_id IN 
    (
    SELECT image_id FROM image_tags WHERE tag_id = 
     (SELECT tag_id FROM tags WHERE tag = @tag1) 
    INTERSECT 
    SELECT image_id FROM image_tags WHERE tag_id = 
     (SELECT tag_id FROM tags WHERE tag = @tag2) 
    INTERSECT 
     .... 
    ) 

Cela permet de sélectionner toutes les images en fonction de l'intersection (correspondant à tous) balises dans image_tags.

Questions connexes