2

「記事」と「タグ」の間に HABTM 関係があります

問題: 'sports' と 'outdoors' の両方のタグを含む記事のみを探していますが、これらのタグの 1 つだけを含む記事は探していません。

私はこれを試しました:

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

...しかし、スポーツのみ、アウトドアのみ、およびスポーツとアウトドアの両方の記事が表示されます

質問使用する適切なクエリは何ですか? (私はMySQLを使用しています)

4

2 に答える 2

1

これを試して:

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
于 2009-12-09T01:00:11.810 に答える
0

一般的な解決策は 2 つあります。

  • 最初のソリューションではGROUP BY、「アウトドア」または「スポーツ」に一致するタグを記事ごとにカウントし、両方のタグを持つグループのみを返します。

    SELECT a.id, a.name
    FROM articles AS a
    INNER JOIN articles_tags AS at ON (a.id = at.article_id)
    INNER JOIN tags AS t ON (t.id = at.tag_id)
    WHERE t.name IN ('outdoors', 'sports')
    GROUP BY a.id
    HAVING COUNT(DISTINCT t.name) = 2;
    

    このソリューションは、一部の人にとっては読みやすいように見え、値の追加はより簡単です。しかしGROUP BY、MySQL でのクエリは、パフォーマンスを損なう一時テーブルを発生させる傾向があります。

  • もう 1つのソリューションは、JOIN個別のタグごとに使用します。内部結合を使用することで、クエリは、指定したすべてのタグに一致する記事に自然に制限されます。

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

    tags.namearticles_tags.(article_id,tag_id)両方に制約があると仮定すると、クエリ修飾子UNIQUEは必要ありません。DISTINCT

    GROUP BYこのタイプのクエリは、適切なインデックスを定義していると仮定すると、ソリューションよりも MySQL でより適切に最適化される傾向があります。


コメントのフォローアップの質問については、次のようにします。

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;

これはまだ「アウトドア」と「スポーツ」の両方のタグを持つ記事のみを検索しますが、これらの記事をさらにすべてのタグに結合します。

これにより、記事ごとに複数の行 (タグごとに 1 行) が返されるため、GROUP BY記事ごとに 1 行に減らすために使用します。 GROUP_CONCAT()それぞれのグループの値のコンマ区切りリストを返します。

于 2009-12-09T17:58:53.057 に答える