MySQL - 在不同条件下多次连接同一张表需要永远

标签 mysql join multiple-tables

简化一下,我有四个表。

ref_TagGroup (top-level descriptive containers for various tags)
ref_Tag (tags with name and unique tagIDs)
ref_Product
ref_TagMap (TagID,Container,ContainerType)
A fifth table, ref_ProductFamily exists but is not directly part of this query.

我使用 ref_TagMap 表将标签映射到产品,还将标签映射到标签组和产品系列。 ContainerType 相应地设置为 PROD/TAGGROUP/PRODFAM。

因此,我想返回标记组、标记名以及标记映射到的产品和产品系列的数量...所以结果如下:

组名 |标记名 |标签命中数

我的问题是,为什么第一个查询以毫秒为单位返回,第二个查询以毫秒为单位返回,但第三个查询(只是添加一个“或”条件以包含标签到产品和标签到系列的映射)需要永远(好吧,无论如何超过十分钟......我还没有让它运行一整夜。)

查询 1:

SELECT ref_taggroup.groupname,ref_tag.tagname,COUNT(DISTINCT IFNULL(ref_product.familyid,ref_product.id + 100000000),ref_product.name) AS 'taghitcnt' 
FROM (ref_taggroup,ref_tag,ref_product) 
LEFT JOIN ref_tagmap GROUPMAP ON GROUPMAP.containerid=ref_taggroup.groupid 
LEFT JOIN ref_tagmap PRODMAP ON PRODMAP.containerid=ref_product.id 
WHERE 
GROUPMAP.tagid=ref_tag.tagid AND GROUPMAP.containertype='TAGGROUP' 
AND
PRODMAP.tagid=ref_tag.tagid AND PRODMAP.containertype='PROD' 
GROUP BY tagname 
ORDER BY groupname,tagname ;

查询 2:

SELECT ref_taggroup.groupname,ref_tag.tagname,COUNT(DISTINCT IFNULL(ref_product.familyid,ref_product.id + 100000000),ref_product.name) AS 'taghitcnt' 
FROM (ref_taggroup,ref_tag,ref_product) 
LEFT JOIN ref_tagmap GROUPMAP ON GROUPMAP.containerid=ref_taggroup.groupid 
LEFT JOIN ref_tagmap PRODFAMMAP ON PRODFAMMAP.containerid=ref_product.familyid 
WHERE 
GROUPMAP.tagid=ref_tag.tagid AND GROUPMAP.containertype='TAGGROUP' 
AND
PRODFAMMAP.tagid=ref_tag.tagid AND PRODFAMMAP.containertype='PRODFAM' 
GROUP BY tagname 
ORDER BY groupname,tagname ;

查询 3:

SELECT ref_taggroup.groupname,ref_tag.tagname,COUNT(DISTINCT IFNULL(ref_product.familyid,ref_product.id + 100000000),ref_product.name) AS 'taghitcnt' 
FROM (ref_taggroup,ref_tag,ref_product) 
LEFT JOIN ref_tagmap GROUPMAP ON GROUPMAP.containerid=ref_taggroup.groupid 
JOIN ref_tagmap PRODMAP ON PRODMAP.containerid=ref_product.id 
JOIN ref_tagmap PRODFAMMAP ON PRODFAMMAP.containerid=ref_product.familyid 
WHERE 
GROUPMAP.tagid=ref_tag.tagid AND GROUPMAP.containertype='TAGGROUP' 
AND
((PRODMAP.tagid=ref_tag.tagid AND PRODMAP.containertype='PROD') 
OR 
(PRODFAMMAP.tagid=ref_tag.tagid AND PRODFAMMAP.containertype='PRODFAM' ))
GROUP BY tagname 
ORDER BY groupname,tagname ;

-- 为了回答可能出现的问题,select 中的 COUNT Distinct ifnull 旨在为归入系列的大量产品返回一条记录,并为不在系列中的每个“独立”产品返回一条记录.此代码在其他查询中运行良好。

我已经尝试对前两个查询执行 UNION,这很有效并且返回速度非常快,但由于其他原因,它并不实用,我不会在这里讨论。

最好的方法是什么?我做错了什么?

谢谢!


添加解释输出

QUERY1                                  
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  GROUPMAP    ALL                 5640    Using where; Using temporary; Using filesort
1   SIMPLE  ref_tag ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.tagid    1   Using index
1   SIMPLE  ref_taggroup    ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.containerid  3   Using index
1   SIMPLE  PRODMAP ALL                 5640    Using where; Using join buffer
1   SIMPLE  ref_product eq_ref  PRIMARY PRIMARY 4   lsslave01.PRODMAP.containerid   1   

QUERY2                                  
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  GROUPMAP    ALL                 5640    Using where; Using temporary; Using filesort
1   SIMPLE  ref_tag ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.tagid    1   Using index
1   SIMPLE  ref_taggroup    ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.containerid  3   Using index
1   SIMPLE  PRODFAMMAP  ALL                 5640    Using where; Using join buffer
1   SIMPLE  ref_product ref FixtureType FixtureType 5   lsslave01.PRODFAMMAP.containerid    39  Using where

QUERY3                                  
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  GROUPMAP    ALL                 5640    Using where; Using temporary; Using filesort
1   SIMPLE  ref_tag ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.tagid    1   Using index
1   SIMPLE  ref_taggroup    ref PRIMARY PRIMARY 4   lsslave01.GROUPMAP.containerid  3   Using index
1   SIMPLE  PRODMAP ALL                 5640    Using join buffer
1   SIMPLE  PRODFAMMAP  ALL                 5640    Using where; Using join buffer
1   SIMPLE  ref_product eq_ref  PRIMARY,FixtureType PRIMARY 4   lsslave01.PRODMAP.containerid   1   Using where

enter code here

为任何感兴趣的人再更新一次: 我终于让上面的第三个查询运行完成。大约用了 1000 秒。将这个时间除以每个查询(1 或 2)运行所花费的时间,我们得到一个大约 6000 的数字......这非常接近我们在开发环境中使用的 ref_tagmap 表的大小(产量大得多)。所以,看起来我们正在针对该表中的每条记录运行一个查询...但我仍然不明白为什么。

任何帮助将不胜感激......我的意思是非常非常感谢。

最佳答案

与其说这是一个“答案”,不如说是一些观察/建议。

首先,我很好奇您是否可以对整数 ID 而不是标签名称进行 GROUP BY?我将更改 ref_TagMap.containertype 字段以保存代表 TAGGROUP、PROD 和 PRODFAM 三个可能值的 tinyint 枚举值。索引的 tinyint 字段应该比字符串值的索引稍快。不过,它可能不会有太大区别,因为它是连接子句中的第二个条件,而且索引值中的分布并不多。

接下来是观察/提醒,当 OR 语句的前半部分经常评估为 FALSE 时,那么你让 MySQL 每次都评估条件的两半。所以你想把最有可能评估为 TRUE 的条件放在第一位(也就是在 OR 之前)。

我怀疑这两个问题中的任何一个都是您的真正问题……尽管第二段中的问题可能起到一定作用。 似乎实现查询 3 的高性能版本的最快方法可能是简单地使用前两个查询的结果填充一个临时表,然后从该临时表中提取以获取您要查找的结果第三个。也许在这样做的时候你会发现为什么第三个查询这么慢。

关于MySQL - 在不同条件下多次连接同一张表需要永远,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9542149/

相关文章:

mysql - 如何提高MySQL join 的效率?

php - 读取 excel 文件并将其存储在 mysql 表中的最佳方式。?我的表包含 4 列

mysql - 如何连接或子查询表示层的表

mysql - 尝试按 desc 按部门顺序计算教授数量

MySQL LEFT JOIN NULL 值

mysql - PHP 从 3 个表中选择

sql - 在一个查询中对多个表进行多个左连接

sql - 如果 id 不存在于另一个表中,则从表中删除

java - 从 BatchUpdateException 确定每行执行失败的原因

php - 在 CakePHP 中更新没有表单数据的数据库