多个标签搜索查询

时间:2012-01-06 18:05:22

标签: mysql

我致力于基于标签的搜索。我有三个表标签(id,name),tagXmedia(id,tag_id,media_id)和media(id,...)。 tagXmedia是标记和媒体表之间的映射表。这是一对多的关系。

我真的可以在如何创建“AND”类型的搜索方面使用一点方向。例如,我需要能够在媒体表中搜索与“home”和“hawaii”标签相关联的条目。

我已经尝试过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")

对此的任何帮助都会非常感激。

4 个答案:

答案 0 :(得分: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子句中的 2

我认为tagXmedia中的所有行都是唯一的。如果不是,则必须将DISTINCT添加到COUNT部分。

答案 1 :(得分: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中。这就是查询有效的原因。

答案 2 :(得分: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

答案 3 :(得分: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