2012-01-06 14 views
8

私はタグベースの検索に取り組んでいます。私は3つのテーブルタグ(id、name)、tagXmedia(id、tag_id、media_id)、およびメディア(id、...)を持っています。 tagXmediaは、タグテーブルとメディアテーブルの間のマッピングテーブルです。これは1対多の関係です。複数のタグの検索クエリ

"AND"タイプの検索を作成する方法については、実際には少し指導しています。たとえば、私は "ホーム"タグと "ハワイ"タグの両方に関連付けられているメディアテーブルのエントリを検索できる必要があります。私は、MySQLで実験してきた

は、このような

SELECT 
    tam.media_id 
FROM 
    tagXmedia tam 
    LEFT JOIN tag ON tag.id = tam.tag_id 
WHERE 
    EXISTS (SELECT * FROM tag WHERE tag.name = "home") 
AND EXISTS (SELECT * FROM tag WHERE tag.name = "hawaii") 

として存在し、この上の任意のヘルプは本当にいただければ幸いです。

答えて

8

次のように動作するはずです。

SELECT media_id 
FROM tagXmedia 
WHERE tag_id IN (SELECT id FROM tag WHERE name IN ('home','hawaii')) 
GROUP BY media_id 
HAVING COUNT(tag_id) = 2; 

2つ以上のタグに一致させたい場合は、簡単に追加できます。 HAVING句のを忘れずに変更してください。

tagXmediaのすべての行が一意であると仮定しました。そうでない場合はをCOUNTに追加する必要があります。

+0

私は別の例を見つけ、動作しますが、あなたはもう少し簡潔です。ありがとう! –

0

は、この文字列で検索してください:

SELECT 
    T1.media_id 
FROM 
    tagXmedia as T1 
INNER JOIN media as T2 
ON T1.media_id =T2.id 
INNER JOIN tag as T3 
ON T1.id = T3.tag_id AND T3.name IN ('home','hawaii') 
GROUP BY T1.media_id 
+0

ネストされた内部結合は、あなたに 'T3.tag_id'へのアクセスを許可しません。 – kba

+0

どうしたの? Ist内部結合はメディアファイルを取得し、2番目の結合は "( 'home'、 'hawaii')という名前のタグを取得します –

+0

どのバージョンのMySQLをテストしましたか? – kba

1
SELECT 
    media1.media_id 
FROM 
    (
     SELECT media_id FROM tagXmedia A INNER JOIN 
     (SELECT id tag_id FROM tag WHERE name='home') B 
     USING (tag_id) 
    ) media1 
    INNER JOIN 
    (
     SELECT media_id FROM tagXmedia C INNER JOIN 
     (SELECT id tag_id FROM tag WHERE name='hawaii') D 
     USING (tag_id) 
    ) media2 
    USING (media_id) 
; 

あなたはtagXmediaでこのインデックスを持っていることを確認してください:ここで

ALTER TABLE tagXmedia ADD UNIQUE INDEX (tag_id,media_id); 

は、テストケースである:ここで

drop database if exists tagmediatest; 
create database tagmediatest; 
use tagmediatest 
CREATE TABLE media 
(
    id int not null auto_increment, 
    stuff varchar(20), 
    PRIMARY KEY (id) 
); 
INSERT INTO media (stuff) VALUES 
('magazine'),('television'),('iphone'), 
('ipad'),('IE9 Browser'),('radio'); 
CREATE TABLE tag 
(
    id int not null auto_increment, 
    name varchar(20), 
    PRIMARY KEY (id), 
    UNIQUE KEY (name) 
); 
INSERT INTO tag (name) VALUES 
('away'),('home'),('jersery city'),('hawaii'),('nyc'); 
CREATE TABLE tagXmedia 
(
    id int not null auto_increment, 
    tag_id INT NOT NULL, 
    media_id INT NOT NULL, 
    PRIMARY KEY (id), 
    UNIQUE KEY (tag_id,media_id) 
); 
INSERT INTO tagXmedia (tag_id,media_id) VALUES 
(1,1),(1,2),(1,3),(1,6), 
(2,1),(2,2),(2,4),(2,5), 
(3,5),(3,4),(3,3),(3,1), 
(4,2),(4,3),(4,5),(4,6), 
(5,2),(5,3),(5,5),(5,4); 
SELECT 
    media1.media_id 
FROM 
    ( 
     SELECT media_id FROM tagXmedia A INNER JOIN 
     (SELECT id tag_id FROM tag WHERE name='home') B 
     USING (tag_id) 
    ) media1 
    INNER JOIN 
    ( 
     SELECT media_id FROM tagXmedia C INNER JOIN 
     (SELECT id tag_id FROM tag WHERE name='hawaii') D 
     USING (tag_id) 
    ) media2 
USING (media_id) 
; 

です結果:

mysql> drop database if exists tagmediatest; 
Query OK, 3 rows affected (0.09 sec) 

mysql> create database tagmediatest; 
Query OK, 1 row affected (0.00 sec) 

mysql> use tagmediatest 
Database changed 
mysql> CREATE TABLE media 
    -> (
    -> id int not null auto_increment, 
    -> stuff varchar(20), 
    -> PRIMARY KEY (id) 
    ->); 
Query OK, 0 rows affected (0.05 sec) 

mysql> INSERT INTO media (stuff) VALUES 
    -> ('magazine'),('television'),('iphone'), 
    -> ('ipad'),('IE9 Browser'),('radio'); 
Query OK, 6 rows affected (0.05 sec) 
Records: 6 Duplicates: 0 Warnings: 0 

mysql> CREATE TABLE tag 
    -> (
    -> id int not null auto_increment, 
    -> name varchar(20), 
    -> PRIMARY KEY (id), 
    -> UNIQUE KEY (name) 
    ->); 
Query OK, 0 rows affected (0.08 sec) 

mysql> INSERT INTO tag (name) VALUES 
    -> ('away'),('home'),('jersery city'),('hawaii'),('nyc'); 
Query OK, 5 rows affected (0.06 sec) 
Records: 5 Duplicates: 0 Warnings: 0 

mysql> CREATE TABLE tagXmedia 
    -> (
    -> id int not null auto_increment, 
    -> tag_id INT NOT NULL, 
    -> media_id INT NOT NULL, 
    -> PRIMARY KEY (id), 
    -> UNIQUE KEY (tag_id,media_id) 
    ->); 
Query OK, 0 rows affected (0.06 sec) 

mysql> INSERT INTO tagXmedia (tag_id,media_id) VALUES 
    -> (1,1),(1,2),(1,3),(1,6), 
    -> (2,1),(2,2),(2,4),(2,5), 
    -> (3,5),(3,4),(3,3),(3,1), 
    -> (4,2),(4,3),(4,5),(4,6), 
    -> (5,2),(5,3),(5,5),(5,4); 
Query OK, 20 rows affected (0.05 sec) 
Records: 20 Duplicates: 0 Warnings: 0 

mysql> SELECT 
    ->  media1.media_id 
    -> FROM 
    ->  (
    ->   SELECT media_id FROM tagXmedia A INNER JOIN 
    ->   (SELECT id tag_id FROM tag WHERE name='home') B 
    ->   USING (tag_id) 
    -> ) media1 
    ->  INNER JOIN 
    ->  (
    ->   SELECT media_id FROM tagXmedia C INNER JOIN 
    ->   (SELECT id tag_id FROM tag WHERE name='hawaii') D 
    ->   USING (tag_id) 
    -> ) media2 
    -> USING (media_id) 
    -> ; 
+----------+ 
| media_id | 
+----------+ 
|  2 | 
|  5 | 
+----------+ 
2 rows in set (0.00 sec) 

mysql> 

tag_id 2と4はmedia_id 2と5に存在することに注意してください。これがクエリが機能する理由です。

+0

これは 'home'または 'hawaii'のいずれかを持つものを返します。 – kba

+0

あなたは正しいです。私は 'USING(media_id)'を忘れてしまった。私は私の答えを更新します。 – RolandoMySQLDBA

1

@ kbaの回答は正しいですが、おそらくもっと効率的なJOINでこれを行うこともできます。

SELECT media_id 
    FROM tagXmedia 
    LEFT JOIN tag ON tag_id = tag.id 
    WHERE tag.name IN ('home', 'hawaii') 
    GROUP BY media_id 
    HAVING COUNT(tag_id) = 2; 

私はmedia_idが、実際のオブジェクトだけでなくを取得したいと同様の問題を抱えていたし、任意のカンマで渡したいタグのリストを分離しました。ここでは、ストアドプロシージャを使用して、私の完全なソリューションです:

CREATE PROCEDURE FindByTag(IN _tags VARCHAR(256)) 
BEGIN 
    DECLARE _length INT; 

    -- Get the length of the list 
    SET _tags = TRIM(BOTH ',' FROM _tags); 
    SET _length = LENGTH(_tags) - LENGTH(REPLACE(_tags, ',', '')) + 1; 

    -- Find media 
    SELECT * FROM media 
    WHERE id IN (
     SELECT media_id FROM tagXmedia 
     LEFT JOIN tag ON tag_id = tag.id 
     WHERE FIND_IN_SET(tag.name, _tags) 
     GROUP BY media_id 
     HAVING COUNT(tag_id) = _length 
    ) 
END 
+0

あなたは "おそらくもっと効率的だ"と言っていますが、実際にこのクエリを@ kba'sに対してベンチマークしましたか? – Daishi

+0

ok、https://stackoverflow.com/questions/2577174/join-vs-sub-queryおよびhttps://stackoverflow.com/questions/141278/subqueries-vs-joinsこれらのページから私の答えを得ました。 'JOIN'のような感じです。 – Daishi

関連する問題