【问题标题】:Multiple tags search query多标签搜索查询
【发布时间】:2012-02-04 10:45:06
【问题描述】:

我从事基于标签的搜索。我有三个表 tag(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")

对此的任何帮助将不胜感激。

【问题讨论】:

    标签: mysql


    【解决方案1】:

    以下应该可以工作。

    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 部分。

    【讨论】:

    • 那行得通,我找到了另一个例子,但你的例子更简洁一些。谢谢!
    【解决方案2】:

    试试这个查询:

    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
    

    【讨论】:

    • 您的嵌套内连接不允许您访问T3.tag_id
    • 有什么问题?第一个内部连接将获得媒体文件,第二个连接将获得名称为“('home','hawaii')”的标签
    • 你在哪个版本的 MySQL 上测试过?
    【解决方案3】:
    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 中。这就是查询有效的原因。

    【讨论】:

    • 这将返回任何具有 'home' 或 'hawaii' 的东西。
    • 你是对的。我忘了USING (media_id)。我会更新我的答案。
    【解决方案4】:

    @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
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-10
    • 2011-07-14
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多