【问题标题】:Complex SELECT query in MySQLMySQL中的复杂SELECT查询
【发布时间】:2012-12-29 14:16:05
【问题描述】:

我有以下表格:

articles: id, title, content
tags: id, tag, tagCategory
tags2articles: id, idTag, idArticle
categories: id, title, someOtherFields

在一个页面中,我需要选择所有具有多个标签的文章。我正在使用这个:

SELECT
   SQL_CALC_FOUND_ROWS a.* 
FROM 
  articles AS a
  JOIN tags2articles AS ta  ON a.id=ta.idArticle
  JOIN tags AS t ON ta.idTag=t.id
WHERE 
  t.id IN (12,13,16) 
GROUP BY a.id
HAVING
  COUNT(DISTINCT t.id)=3

这将选择所有具有 ID 为 12、13 和 16 的标签的文章,并且工作正常。但是,所选文章也可能有其他标签,这些标签可能只针对其中一个或多个。

这里是棘手的部分:我想使用这些标签来制作一些过滤器,所以我需要另一个查询来选择上面文章所具有的所有不同标签。像这样的:

╔═══════╦══════╦═══════════╦════════════════╗
║ TagID ║ Tag  ║ Category  ║ SomeOtherField ║
╠═══════╬══════╬═══════════╬════════════════╣
║ id1   ║ tag1 ║ category1 ║ field1         ║
║ id2   ║ tag2 ║ category2 ║ field2         ║
║ id3   ║ tag3 ║ category1 ║ field1         ║
║ id4   ║ tag4 ║ category3 ║ field3         ║
╚═══════╩══════╩═══════════╩════════════════╝

【问题讨论】:

    标签: mysql sql select join tags


    【解决方案1】:

    使用与您已有的类似查询作为要连接的派生表(但没有所有 a.* 列),您可以对 tags2articles 执行 INNER JOIN 以获取这些文章 ID 具有的剩余标签.

    这应该会导致任何匹配文章持有的所有标签的不同列表。

    SELECT 
      DISTINCT
      t.id,
      t.tag, 
      c.title AS Category
    FROM
      tags2Articles t2a 
      INNER JOIN tags t ON t.id = t2a.idTag
      INNER JOIN categories c ON t.tagCategory = c.id
      /* Subquery join returns article ids having all 3 tags you filtered */
      /* Joining against tags2articles again will get the remaining tags for these articles */
      INNER JOIN (
        SELECT
         a.id 
        FROM 
         articles AS a
         JOIN tags2articles AS ta  ON a.id=ta.idArticle
         JOIN tags AS tsub ON ta.idTag=tsub.id
        WHERE 
          tsub.id IN (12,13,16) 
        GROUP BY a.id
        HAVING COUNT(DISTINCT tsub.id)=3 
      ) asub ON t2a.idArticle = asub.id
    

    【讨论】:

    • 您可以将tsub 排除在子查询之外,直接使用idTag
    【解决方案2】:

    这是对迈克尔答案的重写,删除了无关的连接:

    SELECT DISTINCT t.id, t.tag, c.title AS Category
    FROM tags2Articles t2a INNER JOIN
         tags t
         ON t.id = t2a.idTag INNER JOIN
         categories c ON t.tagCategory = c.id inner join
         /* Subquery join returns article ids having all 3 tags you filtered */
         /* Joining against tags2articles again will get the remaining tags for these articles */
         (SELECT t2a.idArticle
          FROM tags2articles t2a
          WHERE t2a.idTag IN (12,13,16) 
          GROUP BY t2a.idArticle
          HAVING COUNT(DISTINCT t2a.idTag)=3 
        ) asub
        ON t2a.idArticle = asub.idArticle
    

    【讨论】:

    • 很好的观察,只是它是“GROUP BY t2a.iditem”而不是“GROUP BY t2a.id”
    【解决方案3】:

    这可能看起来很难看,但它可能会更快

    SELECT a.*
    FROM articles AS a
    WHERE 1=1
    AND EXISTS (
      SELECT *
      FROM tags2articles AS ta   
      JOIN tags AS t ON ta.idTag=t.id
      WHERE  a.id=ta.idArticle AND t.id = 12
      )
    AND EXISTS (
      SELECT *
      FROM tags2articles AS ta  
      JOIN tags AS t ON ta.idTag=t.id
      WHERE  a.id=ta.idArticle AND t.id = 13
      )
    AND EXISTS (
      SELECT *
      FROM tags2articles AS ta  
      JOIN tags AS t ON ta.idTag=t.id
      WHERE  a.id=ta.idArticle AND t.id = 16
      )
      ;
    

    顺便说一句:由于ta.idTag=t.id,带有标签表的连接可能会被省略(给定适当的 FK 常量)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-13
      • 2021-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-28
      相关资源
      最近更新 更多