2

これは十分に一般的であり、1 つの SQL クエリ (MySQL) でこれを実行する「最良の」方法を探しています。

itemsテーブル、テーブル、テーブルの 3 つのテーブルがlinkerありtagsます。アイテムは複数回タグ付けできるため、リンカーは単純な外部キー リンカー テーブルです。

items   | linker  | tags  
--------+---------+-------
item_id | item_id | tag_id
...     | tag_id  | name  
--------+---------+-------

単一のタグを簡単に検索できitemsますが、2 つ以上の特定のタグを持つアイテムを検索するにはどうすればよいですか?

SELECT *, `tags`.`name`
FROM `items`
LEFT OUTER JOIN `linker` USING (`item_id`)
LEFT OUTER JOIN `tags` USING (`tag_id`)
WHERE `tags`.`name` = "tag-a"

正気の人はどのように 2 つ以上のタグの検索を実行しますか?アイテムにはすべてのタグが必要です。つまり、ANDクエリですか?


編集:私がこれまでに持っているのは次のとおりです。これは機能し、遅くはないようですが、クレイジーに見えます:

SELECT `items`.* FROM `items`
LEFT OUTER JOIN `linker` USING (`item_id`)
LEFT OUTER JOIN `tags` USING (`tag_id`)
WHERE (
        `item_id` IN (SELECT item_id FROM linker LEFT JOIN tags USING (tag_id) WHERE name = "tag-a")
    AND `item_id` IN (SELECT item_id FROM linker LEFT JOIN tags USING (tag_id) WHERE name = "tag-b")
    AND `item_id` IN (SELECT item_id FROM linker LEFT JOIN tags USING (tag_id) WHERE name = "tag-c")
    AND `item_stuff` = "whatever"
)
4

4 に答える 4

1

リンカー テーブルの PK が (item_id,tag_id) であると仮定すると、次のようになります。

select *
  from items
  where item_id in (
    select item_id
      from linker
      join tags using(tag_id)
     where name in ('tag1', 'tag2', 'tag3')
     group by item_id
     having count(tag_id)=3
  )
;

上記のクエリは、保守が容易なはずです。必要なタグ名を簡単に追加または削除できます。所有数がリスト内の名前の数と一致することを確認するだけです。

リンカー テーブルの PK が (item_id,tag_id) でない場合、having 句を に変更する必要がhaving count(distinct tag_id)=3ありますが、重複する (item_id,tag_id) ペアがいくつ存在するかによっては、クエリのパフォーマンスが低下する可能性があります。

上記のもう 1 つの優れた機能は、どの項目が次のタグのリスト (「tag1」、「tag2」、「tag3」) の少なくとも 2 つに関連付けられているかなどの質問に簡単に回答できることです。所有カウントを正しい値に設定するだけです。

于 2012-12-30T04:31:41.087 に答える
0

質問を言い換えると、いくつかのリストにitemsすべてが含まれているテーブルのすべての列tagsが必要ですが、それは正しいですか?もしそうなら、私はあなたがtagsそれぞれのためにあなたのテーブルに参加し、のINNER JOIN代わりに使用する必要があると思いますLEFT OUTER JOIN。このようなもの:

SELECT DISTINCT `items`.* 
FROM   `items` a
JOIN   `linker` b 
ON     b.item_id=a.item_id

JOIN   `tags` c1
ON     c1.tag_id=b.tag_id
   and c1.name = "tag-a"

JOIN   `tags` c2
ON     c2.tag_id=b.tag_id
   and c2.name = "tag-a"

JOIN   `tags` c3
ON     c3.tag_id=b.tag_id
   and c3.name = "tag-c"

を使用するINNER JOINと、3つのタグすべてを持つ行のみが選択されます。可変数のタグを使用してこれをどのように行うかはわかりません(これが本当に必要なことだと思います)。

于 2012-12-30T01:06:57.090 に答える
0

もちろん、これはすでに質問されています: How to filter SQL results in a has-many-through relationship

私の暫定的な解決策は最速の1つであることが判明しました(リンクされた質問の4番)、ここにあります:

SELECT *
FROM `items`
WHERE (
        `item_id` IN (SELECT item_id FROM linker INNER JOIN tags USING (tag_id) WHERE name = "tag-a")
    AND `item_id` IN (SELECT item_id FROM linker INNER JOIN tags USING (tag_id) WHERE name = "tag-b")
    AND `item_id` IN (SELECT item_id FROM linker INNER JOIN tags USING (tag_id) WHERE name = "tag-c")
    AND `item_stuff` = "whatever"
)
于 2012-12-30T01:20:25.570 に答える
0

私が正しく理解していれば (私にはわかりません :) ... )、特定の文字列を含む結果を見つけたいとします (正規表現検索のように)。

RLIKEあなたは機能を試すことができます

SELECT *, `tags`.`name`
FROM `items`
LEFT OUTER JOIN `linker` USING (`item_id`)
LEFT OUTER JOIN `tags` USING (`tag_id`)
WHERE `tags`.`name` RLIKE("tag-a"|"tag-b")

これがあなたの言いたいことだと思いますが、そうではないかもしれません:

http://dev.mysql.com/doc/refman/5.0/en/regexp.html


または、各エントリにタグが 1 つしかない場合は、次のように使用しINます。

SELECT *, `tags`.`name`
FROM `items`
LEFT OUTER JOIN `linker` USING (`item_id`)
LEFT OUTER JOIN `tags` USING (`tag_id`)
WHERE `tags`.`name` IN ("tag-a","tag-b")

http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_in


そして、なぜ基本だけではないのですかOR

 WHERE `tags`.`name` = "tag-a" OR `tags`.`name` = "tag-b"

あなたの目標を正しく理解していることを願っています。そうでない場合はお知らせください。

編集あなたの質問の一部を読み間違えました...私は正気ではないかもしれませんが、これが私を失格にしないことを願っています:P

于 2012-12-30T00:00:36.187 に答える