慢MySQL IN查询 - 如何转换为JOIN?

时间:2012-04-30 17:19:59

标签: mysql sql database join

我目前的MySQL查询在我的应用程序中运行最多需要10秒钟:

SELECT tagid, tag FROM tags WHERE tagid IN 
(SELECT DISTINCT tagid FROM news_tags WHERE newsid IN 
(SELECT newsid FROM news_tags WHERE tagid IN (16,32)
GROUP BY newsid HAVING COUNT(newsid)>=2)) 
AND tagid NOT IN (16,32) ORDER BY level, tagid

使用的表是:

  • news_tags,列newsidtagid
  • tags,列tagidtaglevel

查询的目的是查找已使用tagid 16 32标记的“新闻”项目,然后查找其他标记这些新闻项目也已标记为,为了允许用户进一步缩小具有更具体标签组合的“新闻”项目。最终目标是从tag表中获取剩余的相关tagidtags列。

我尝试过等同JOIN的不同尝试,但未能选择附加了提供标签的新闻项上的所有剩余tagid

这是我的EXPLAIN SQL结果,如果它们指向另一个我不知道的缓慢原因:

id|select_type       |table    |type          |possible_keys|key    |key_len|ref |rows|Extra
 1|PRIMARY           |tags     |range         |PRIMARY      |PRIMARY|      4|NULL|  55|Using where; Using filesort
 2|DEPENDENT SUBQUERY|news_tags|index_subquery|tagid        |tagid  |      4|func|  26|Using index; Using where
 3|DEPENDENT SUBQUERY|news_tags|index         |tagid        |PRIMARY|      8|NULL|  11|Using where; Using index

只是为了澄清问题:我希望剩下的标签用于标有BOTH标签16和32的新闻标签,而不是16或32.对不起任何混淆。

3 个答案:

答案 0 :(得分:2)

SELECT DISTINCT tags.tagid, tags.tag
FROM
       tags                             -- tags from the ...
  JOIN news_tags AS n0 USING (tagid)    -- ... news items tagged with ...
  JOIN news_tags AS n1 USING (newsid)   -- ... tagid = 16 and ...
  JOIN news_tags AS n2 USING (newsid)   -- ... tagid = 32
WHERE
  n1.tagid = 16 AND n2.tagid = 32
  AND tags.tagid NOT IN (16,32)         -- not the tags we already know about
ORDER BY tags.level, tags.tagid

答案 1 :(得分:1)

编辑:我的查询严格基于提供的sql OP,只是试图像问题标题一样加快查询速度。

SELECT DISTINCT t.tagid, t.tag FROM tags AS t
JOIN            news_tags AS nt1 USING (tagid) 
JOIN            news_tags AS nt2 USING (newsid)
WHERE           nt2.tagid IN (16, 32) AND t.tagid NOT IN (16, 32) 
GROUP BY        nt2.newsid HAVING COUNT(nt2.newsid)>=2
ORDER BY        t.level, t.tagid

答案 2 :(得分:0)

我最终提出了一个快速查询,使用JOINS而不是IN语句解决了这个问题:

SELECT tags.tagid,tags.tag FROM tags 
INNER JOIN (SELECT DISTINCT news_tags.tagid FROM news_tags
INNER JOIN (SELECT newsid FROM news_tags WHERE tagid IN (16,32) 
GROUP BY newsid HAVING count(newsid) >= 2) tagged_news 
ON news_tags.newsid = tagged_news.newsid 
WHERE news_tags.tagid NOT IN (16,32)) rem_tags
ON tags.tagid = rem_tags.tagid
ORDER BY level, tagid

这显然不像eggyal的解决方案那样干净或优雅,所以我最终在我的应用程序中采用了他的解决方案。

我希望听到更多客观原因(除了优雅)之外,为什么eggyval的解决方案会优先于上述SQL语句,既可以找到问题的最佳SQL语句,也可以学习将来。到目前为止,感谢所有帮助。