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

时间:2012-03-03 00:15:34

标签: 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表将标签映射到产品,还将标签映射到TagGroups以及产品系列。 ContainerType相应地设置为PROD / TAGGROUP / PRODFAM。

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

GroupName | TagName | TagHitCnt

我的问题是,为什么第一个查询以毫秒为单位返回,第二个查询以毫秒为单位返回,但是第三个查询(仅添加“OR”条件以将标记包含到产品中并标记到族映射)需要永远(好吧,不过十分钟......我还没有让它整晚都运行。)

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

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

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

- 要回答可能出现的问题,选择中的COUNT Distinct ifnull旨在为大量分组为系列的产品返回一条记录,并为每个不属于系列的“独立”产品返回一条记录。此代码适用于其他查询。

我已经尝试过在前两个查询中执行UNION,这样可以很快地恢复,但是由于其他原因我不会在这里进行,这是不切实际的。

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

谢谢!


添加EXPLAIN OUTPUT

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表的大小(生产量大得多)。所以,看起来我们正在对该表中的每条记录运行一个查询......但我仍然看不出原因。

任何帮助都会非常感激......我的意思是认真,非常感谢。

1 个答案:

答案 0 :(得分:0)

这不是一个“答案”,而是一些观察/建议。

首先,我很好奇你是否可以在整数ID而不是标签名称上使用GROUP BY?我将更改ref_TagMap.containertype字段以保存tinyint枚举值,表示TAGGROUP,PROD和PRODFAM的三个可能值。索引的tinyint字段应该比字符串值的索引稍快。它可能没有多大区别但是因为它是join子句中的第二个条件,并且索引值中没有那么大的差异。

接下来是观察/提醒,当OR语句的前半部分经常评估为FALSE时,那么你每次都会让MySQL评估条件的两半。因此,您希望首先将最有可能评估的条件设置为TRUE(也就是在OR之前)。

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