4

このテーブルがあるとしましょう:

item_id tag_id
------- ------
1       1
1       2
2       2
2       3

おそらく想像できると思いますが、これはいくつかのアイテムとそれらに属するタグへの参照を含むテーブルです。アイテムには複数のタグを付けることができ、複数のアイテムに対して 1 つのタグを選択できます。

特定のタグ コレクション (f.ex. tag_id = 50、73、および 119) と、id (によって参照されるitem_id) を持つ「items」テーブルもあるとします。

私に与える効率的なクエリはあり ますか:

  1. それらのタグを持つアイテムの数
  2. アイテム自体?

私が試したこと

SELECT COUNT(*) FROM
(
SELECT COUNT(*) AS c FROM items_tags it JOIN items i ON i.id = it.item_id
WHERE (tag_id=7 OR tag_id=95 OR tag_id=150) AND `status`='active'
GROUP BY item_id
) t1 WHERE c=3 <-- c= number of tags

私は両方の結果を得ることができますが、非常に(そう思われる)非効率的なクエリを使用します。EXPLAIN で検討した結果、OR によって指定された「範囲」を取り除きたいと考えています。

問題の改善:問題は、さまざまなタグ ID を 900 回以上反復処理する、非常に貧弱に記述された PHP フレームワークが与えられたことです。1 つ以上の固定 ID (選択されたタグ) があり、900 以上のタグすべてを反復して、指定されたタグと反復されたタグに共通するアイテムの出現回数を見つけるとします (これは、検索し、指定されたすべてのタグに 1 を加えた要素のみを表示します)。

指定されたコードは次のように機能します。1 つ以上のタグを選択すると、それらの ID がクエリ文字列に入ります。タグ 54 と 77 を選択したとします。コードは、タグ 54 と 77 の両方を持つアイテムのすべてのアイテム ID を検索し、それらを 1 つずつリストする必要があります。「選択されたタグを持つアイテム」リストを取得します。

次に、検索を絞り込むための選択肢を提供します。奇妙な部分があります。PHP コードは 900 以上のタグすべてを循環し、反復ごとにタグを取得し、すべてのタグを持つアイテムの数を数えます 54、77そして反復中のもの。カウントが 0 より大きい場合は、タグの名前とカウント数が表示され、選択したタグへのリンクを持たないアイテムを持つすべてのタグが除外されます。

それほど「集中的」でない方法で同じ結果を達成できればよいでしょう。

4

1 に答える 1

3

すべてのタグに一致するアイテム ID のリストを取得するには、次のクエリを使用できます。

SELECT items.id
FROM items
JOIN items_tags ON items.id = items_tags.item_id
WHERE (items_tags.tag_id IN (7,95,150))
  AND (items.status = 'active')
GROUP BY items.id
HAVING COUNT(DISTINCT items_tags.tag_id) = 3

同じアイテムにタグが重複していないことが確実な場合は、効率のために にCOUNT(DISTINCT items_tags.tag_id)置き換えることができます。COUNT(*)

これらのアイテムの数を取得するには、これを COUNT クエリでラップします。

SELECT COUNT(*)
FROM (
  SELECT items.id
  ...
) t

アイテムのリストを取得するには、次の SELECT クエリでラップします。

SELECT *
FROM items
WHERE id IN (
  SELECT items.id ...
)

アップデート

元のリストと組み合わせたときに残りの各タグのアイテム数を取得するには、次のようにします。

SELECT tag_id, COUNT(DISTINCT item_id)
FROM items_tags
WHERE item_id IN (
  SELECT items.id
  ...
)
  AND tag_id NOT IN (7,95,150)
GROUP BY tag_id
于 2013-03-18T18:42:10.533 に答える