2012-01-06 121 views
8

我工作在基於標籤的搜索。我有三個表標記(id,名稱),tagXmedia(id,tag_id,media_id)和媒體(id,...)。 tagXmedia是標籤和媒體表之間的映射表。這是一對多的關係。多標籤搜索查詢

我真的可以對如何創建一個「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; 

如果你希望它不僅僅匹配兩個標籤,你可以很容易地添加它們。請記住更改HAVING條款中的。

我認爲tagXmedia中的所有行都是唯一的。如果他們不是,您將不得不將DISTINCT添加到COUNT部分。

+0

這有效,我找到了另一個例子,但你的是一個更簡潔。謝謝! – 2012-01-06 18:44:07

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 2012-01-06 19:06:50

+0

什麼問題? Ist內部連接將獲得媒體文件,第二個連接將獲得具有名稱的標籤「('home','hawaii')」 – 2012-01-06 19:13:51

+0

您測試過哪個版本的MySQL? – kba 2012-01-06 22:13:47

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

這將返回任何有'家'或'夏威夷'的任何東西。 – kba 2012-01-06 19:08:10

+0

你說得對。我忘記了'USING(media_id)'。我會更新我的答案。 – RolandoMySQLDBA 2012-01-06 19:31:04

1

@ kba的回答是正確的,但您也可以使用JOIN來做到這一點,該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的? – Daishi 2018-03-06 16:57:47

+0

好的,從這些頁面獲得我的答案https://stackoverflow.com/questions/2577174/join-vs-sub-query和https://stackoverflow.com/questions/141278/subqueries-vs-joins。看起來像「JOIN」是要走的路。 – Daishi 2018-03-06 17:00:51