MySQL中的复杂SELECT查询

时间:2012-12-29 14:16:05

标签: mysql sql select join tags

我有以下表格:

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         ║
╚═══════╩══════╩═══════════╩════════════════╝

3 个答案:

答案 0 :(得分:5)

使用您已经拥有的类似查询作为要加入的派生表(但没有所有a.*列),您可以对INNER JOIN执行tags2articles以获得剩余的那些文章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

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

答案 2 :(得分:0)

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

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
  )
  ;

BTW:从ta.idTag=t.id开始,可能会省略与tha标签表的连接(给定正确的FK约束)。