【问题标题】:Tag based SQL query基于标签的 SQL 查询
【发布时间】:2013-02-04 09:09:49
【问题描述】:

我已经有一段时间没有做任何 SQL 了,我不确定这个问题是否有一个简单的解决方案。我也是一个菜鸟。

我正在尝试建立一个图片库,允许用户使用标签来搜索图片,然后单击其他标签来优化搜索并减少结果数量,但我对查询有很大的问题参与。

这是我当前数据库结构的简化版本:

(2个表和一个额外的多对多链接表)

CREATE TABLE images(
   image_id INT(12) AUTO_INCREMENT,
   image_name VARCHAR(128),
   PRIMARY KEY(image_id)
)ENGINE= INNODB;

CREATE TABLE tags(
   tag_name VARCHAR(64) NOT NULL,
   PRIMARY KEY(tag_name)
)ENGINE= INNODB;

CREATE TABLE images_tags_link(
   image_id_fk INT(12),
   tag_name_fk VARCHAR(64) NOT NULL,
   PRIMARY KEY(image_id_fk,tag_name_fk),
   FOREIGN KEY(image_id_fk) REFERENCES images(image_id),
   FOREIGN KEY(tag_name_fk) REFERENCES tags(tag_name)
)ENGINE= INNODB;

样本数据:

 ===images===
 ___________________________       
| image_id |  image_name    |     
|----------|----------------|     
|     1    |  image_001.jpg |     
|     2    |  image_002.jpg |     
|     3    |  image_003.png |     
|     4    |  image_004.jpg |     
|     5    |  image_005.gif |     
 ---------------------------                                     
 ===tags===                                 
 _______________
|    tag_name   |
|---------------|
| Landscape     |
| Portrait      |
| Illustration  |
| Photo         |
| Red           |
| Blue          |
| Character     |
| Structure     |
 ---------------
===images_tags_link===
 ________________________________
| image_id_fk | tag_name_ fk     |
|-------------|------------------|
|      1      |    Landscape     |
|      1      |    Illustration  |
|      1      |    Blue          |
|      2      |    Blue          |
|      2      |    structure     |
|      2      |    Landscape     |
|      3      |    Illustration  |
|      4      |    Red           |
|      4      |    Portrait      |
|      4      |    Character     |
|      5      |    Blue          |
|      5      |    Photo         |
 --------------------------------

我的问题在于以下查询:

我正在寻找一个查询,它可以从 IMAGES 表中选择所有包含所有用户列出的标签的“image_names”,例如,用户可以搜索“Blue”和“Landscape”标签,它应该只输出image_names 'image_001.jpg' AND 'image_002.jpg'。

===输入===

用户选择的标签:( 'Blue' , 'Landscape' )

===输出===

具有所有列出标签的图像名称:('image_001.jpg','image_002.jpg')

提前致谢。

【问题讨论】:

    标签: mysql sql many-to-many subquery


    【解决方案1】:

    这就是他们所谓的Relation Division,这是一种方法:

    SELECT i.*
    FROM Images i 
    INNER JOIN
    (
       SELECT image_id_fk
       FROM images_tags_link
       WHERE tag_name_fk IN ('Blue' , 'Landscape')
       GROUP BY image_id_fk 
       HAVING COUNT(DISTINCT tag_name_fk) = 2
    ) t ON i.image_id = t.image_id_fk;
    

    SQL Fiddle Demo

    这会给你:

    | IMAGE_ID |    IMAGE_NAME |
    ----------------------------
    |        1 | image_001.jpg |
    |        2 | image_002.jpg |
    

    这个查询背后的想法是:

       GROUP BY image_id_fk 
       HAVING COUNT(DISTINCT tag_name_fk) = 2
    

    在子查询中,这将确保任何图像都有两个标签,如果它只有一个,则COUNT将为0,因此将被淘汰。

    【讨论】:

    • 这个也有效,但我不知道为什么。感谢您提供我需要阅读的链接。
    【解决方案2】:

    两种简单的方法。

    以下任意一种,取决于所需的列、可能的标签数量等。

    SELECT *
    FROM images
    INNER JOIN images_tags_link a ON images.image_id = a.image_id_fk AND a.tag_name_fk = 'Blue'
    INNER JOIN images_tags_link b ON images.image_id = b.image_id_fk AND b.tag_name_fk = 'Landscape'
    
    
    SELECT images.image_id, images.image_name, COUNT(*) AS tag_count
    FROM images
    INNER JOIN images_tags_link a ON images.image_id = a.image_id_fk 
    WHERE a.tag_name_fk IN ('Blue', 'Landscape')
    GROUP BY images.image_id, images.image_name
    HAVING tag_count = 2
    

    【讨论】:

    • 感谢您的帮助,我设法让这两个查询正常工作。
    • 这里应该说第二个query或者gamal的回答比较快。
    【解决方案3】:

    这是另一个使用 EXISTS 的示例:

    SELECT *
    FROM Images I
    WHERE EXISTS (
       SELECT image_id_fk
       FROM Images_Tags_Link I2 
       WHERE I2.image_id_fk = I.Image_Id
          AND tag_name_fk IN ('Blue' , 'Landscape')
       GROUP BY I2.image_id_fk
       HAVING COUNT(DISTINCT I2.tag_name_fk) > 1)
    

    还有一个使用 IN:

    SELECT *
    FROM Images I
    WHERE Image_Id IN (
       SELECT image_id_fk
       FROM Images_Tags_Link I2 
       WHERE I2.image_id_fk = I.Image_Id
          AND tag_name_fk IN ('Blue' , 'Landscape')
       GROUP BY I2.image_id_fk
       HAVING COUNT(DISTINCT I2.tag_name_fk) > 1)
    

    祝你好运。

    【讨论】:

      【解决方案4】:

      试试这个更简单的方法


      选择
      图像名称
      来自
      images_tags_link
      tag_name_fk in ('Blue','Landscape')

      【讨论】:

      • 我尝试了与之前类似的查询。对我来说,问题是它不断向我输出具有一个或两个标签的图像,而不仅仅是同时具有两个标签的图像。
      猜你喜欢
      • 1970-01-01
      • 2016-12-21
      • 1970-01-01
      • 1970-01-01
      • 2022-01-27
      • 2012-09-28
      • 2012-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多