2009-12-09 4 views
2

J'ai une relation HABTM entre les « articles » et « tags »HABTM Interrogation aide

Problème: Je ne la recherche d'articles avec les deux l'étiquette « sport » et « plein air », mais pas les articles avec seulement un de ces tags.

J'ai essayé ceci:

SELECT DISTINCT article.id, article.name FROM articles 
inner JOIN tags ON (tags.name IN ('outdoors', 'sports') 
inner JOIN articles_tags ON articles_tags.article_id = article.id AND articles_tags.tag_id = tags.id 

... mais il me fait des articles qui sont dans les sports que, seulement à l'extérieur et les sports + extérieur

Question quelle est la requête droit d'utiliser ? (J'utilise MySQL)

Répondre

0

Il existe deux solutions communes.

  • La première solution utilise GROUP BY compter les balises par l'article qui correspondent à « plein air » ou « sport », puis retourne seuls les groupes qui ont les deux balises. Cette solution semble plus lisible pour certaines personnes, et l'ajout de valeurs est plus simple. Mais les requêtes GROUP BY dans MySQL ont tendance à encourir une table temporaire qui nuit aux performances.

  • L'autre solution utilise un JOIN par étiquette distincte. En utilisant des jointures internes, la requête se restreint naturellement aux articles qui correspondent à tous les tags que vous spécifiez.

    SELECT a.id, a.name 
    FROM articles AS a 
    INNER JOIN articles_tags AS at1 ON (a.id = at1.article_id) 
    INNER JOIN tags AS t1 ON (t1.id = at1.tag_id AND t1.name = 'outdoors') 
    INNER JOIN articles_tags AS at2 ON (a.id = at2.article_id) 
    INNER JOIN tags AS t2 ON (t2.id = at2.article_id AND t2.name = 'sports'); 
    

    En supposant tags.name et articles_tags.(article_id,tag_id) les deux ont UNIQUE contraintes, vous ne devriez pas avoir besoin d'un modificateur de requête DISTINCT.

    Ce type de requête tend à mieux optimiser MySQL que la solution GROUP BY, supposant que vous avez défini les index appropriés.


Re votre question de suivi dans le commentaire, je ferais quelque chose comme ceci:

SELECT a.id, a.name, GROUP_CONCAT(t3.tag) AS all_tags 
FROM articles AS a 
INNER JOIN articles_tags AS at1 ON (a.id = at1.article_id) 
INNER JOIN tags AS t1 ON (t1.id = at1.tag_id AND t1.name = 'outdoors') 
INNER JOIN articles_tags AS at2 ON (a.id = at2.article_id) 
INNER JOIN tags AS t2 ON (t2.id = at2.article_id AND t2.name = 'sports'); 
INNER JOIN articles_tags AS at3 ON (a.id = at3.article_id) 
INNER JOIN tags AS t3 ON (t3.id = at3.article_id); 
GROUP BY a.id; 

Cela trouve encore que des articles qui ont à la fois des étiquettes 'plein air' et 'sports', mais puis il rejoint ces articles à tous ses tags.

Cela retournera plusieurs lignes par article (un pour chaque tag), nous utiliserons alors GROUP BY pour réduire à nouveau une seule ligne par article. GROUP_CONCAT() renvoie une liste des valeurs séparées par des virgules dans le groupe respectif.

+0

merci pour l'explication ... Aussi, est-il possible dans cette requête de renvoyer tous les tags associés aux articles correspondants? – cardflopper

1

Essayez ceci:

SELECT a1.id, a1.name FROM articles a1 
    JOIN tags t1 ON t1.name ='outdoors' 
    JOIN articles_tags at1 ON at1.article_id = a1.id AND at1.tag_id = t1.id 
    JOIN tags t2 ON t2.name = 'sports' 
    JOIN articles_tags at2 ON at2.article_id = a1.id AND at2.tag_id = t2.id