如何通过多个连接加速MySQL查询

时间:2010-10-21 16:13:08

标签: sql mysql join

这是我的问题,我正在选择并进行多次连接以获取正确的项目...它会提取相当数量的行,超过100,000。当日期范围设置为1年时,此查询需要5分钟以上。

我不知道是否可能,但我担心用户可能会将日期范围延长至十年并使其崩溃。

任何人都知道如何加快速度吗?这是查询。

SELECT DISTINCT t1.first_name, t1.last_name, t1.email 
FROM table1 AS t1 
INNER JOIN table2 AS t2 ON t1.CU_id = t2.O_cid 
INNER JOIN table3 AS t3 ON t2.O_ref = t3.I_oref 
INNER JOIN table4 AS t4 ON t3.I_pid = t4.P_id 
INNER JOIN table5 AS t5 ON t4.P_cat = t5.C_id 
WHERE t1.subscribe =1 
AND t1.Cdate >= $startDate
AND t1.Cdate <= $endDate
AND t5.store =2

我对mysql不是最好的,所以任何帮助都会受到赞赏!

提前致谢!

更新

以下是您要求的解释

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  t5  ref     PRIMARY,C_store_type,C_id,C_store_type_2    C_store_type_2  1   const   101     Using temporary
1   SIMPLE  t4  ref     PRIMARY,P_cat   P_cat   5   alphacom.t5.C_id    326     Using where
1   SIMPLE  t3  ref     I_pid,I_oref    I_pid   4   alphacom.t4.P_id    31   
1   SIMPLE  t2  eq_ref  O_ref,O_cid     O_ref   28  alphacom.t3.I_oref  1    
1   SIMPLE  t1  eq_ref  PRIMARY     PRIMARY     4   alphacom.t2.O_cid   1   Using where

我还为table5行和table4行添加了一个索引,因为它们并没有真正改变,但是其他表每月大约有500-1000个条目...我听说你应该为一个有这个表的表添加一个索引许多新条目....这是真的吗?

7 个答案:

答案 0 :(得分:13)

我会尝试以下方法:

首先,确保下列表和列上有索引(括号中的每组列都应该是一个单独的索引):

table1 : (subscribe, CDate)
         (CU_id)
table2 : (O_cid)
         (O_ref)
table3 : (I_oref)
         (I_pid)
table4 : (P_id)
         (P_cat)
table5 : (C_id, store)

其次,如果添加上述索引并没有像您希望的那样改进,请尝试将查询重写为

SELECT DISTINCT t1.first_name, t1.last_name, t1.email FROM
  (SELECT CU_id, t1.first_name, t1.last_name, t1.email
     FROM table1
     WHERE subscribe = 1 AND
           CDate >= $startDate AND
           CDate <= $endDate) AS t1
  INNER JOIN table2 AS t2
    ON t1.CU_id = t2.O_cid   
  INNER JOIN table3 AS t3
    ON t2.O_ref = t3.I_oref   
  INNER JOIN table4 AS t4
    ON t3.I_pid = t4.P_id   
  INNER JOIN (SELECT C_id FROM table5 WHERE store = 2) AS t5
    ON t4.P_cat = t5.C_id

我希望这里第一个子选择会显着减少要考虑加入的行数,希望使后续连接的工作量减少。同样在table5上第二个子选择背后的推理。

无论如何,搞乱它。我的意思是,最终它只是一个SELECT - 你不能用它真的伤害任何东西。检查每个不同排列产生的计划,并试图找出每个排列的好坏。

分享并享受。

答案 1 :(得分:8)

确保您的日期列和您加入的所有列都已编入索引。

在你的日期做一个不等的运算符意味着它检查每一行,这本质上比等价的慢。

此外,使用DISTINCT可以为优化程序在后台运行的逻辑添加额外的比较。如果可能的话,消除它。

答案 2 :(得分:3)

好吧,首先,创建一个子查询来将table1抽取到你真正想要加入的所有记录中的记录......

SELECT DISTINCT t1.first_name, t1.last_name, t1.email  
FROM (  
SELECT first_name, last_name, email, CU_id FROM table1 WHERE  
table1.subscribe = 1  
AND table1.Cdate >= $startDate  
AND table1.Cdate <= $endDate  
) AS t1  
INNER JOIN table2 AS t2 ON t1.CU_id = t2.O_cid  
INNER JOIN table3 AS t3 ON t2.O_ref = t3.I_oref  
INNER JOIN table4 AS t4 ON t3.I_pid = t4.P_id  
INNER JOIN table5 AS t5 ON t4.P_cat = t5.C_id  
WHERE t5.store = 2

然后开始考虑修改连接的方向性。

此外,如果t5.store只是非常罕见的2,那么请翻转这个想法:构建t5子查询,然后将其连接回来,然后反复加入。

答案 3 :(得分:2)

目前,您的查询返回table2-table5上的所有匹配行,只是为了确定t5.store = 2.如果table2-table5中的任何一行的行数明显高于table1,这可能会大大增加数量处理的行 - 因此,以下查询可能表现得更好:

SELECT DISTINCT t1.first_name, t1.last_name, t1.email 
FROM table1 AS t1 
WHERE t1.subscribe =1 
AND t1.Cdate >= $startDate
AND t1.Cdate <= $endDate
AND EXISTS
(SELECT NULL FROM table2 AS t2
INNER JOIN table3 AS t3 ON t2.O_ref = t3.I_oref 
INNER JOIN table4 AS t4 ON t3.I_pid = t4.P_id 
INNER JOIN table5 AS t5 ON t4.P_cat = t5.C_id AND t5.store =2
WHERE t1.CU_id = t2.O_cid);

答案 4 :(得分:1)

尝试在您加入的字段上添加索引。它可能会也可能不会提高性能。

此外,它还取决于您使用的引擎。如果您使用的是InnoDB,请检查您的配置参数。我遇到了类似的问题,因为innodb的默认配置不会像myisam的默认配置那样扩展。

答案 5 :(得分:1)

正如大家所说,确保你有索引。

您还可以检查您的服务器是否设置正确,以便它可以在内存中包含更多可能是整个数据集。

没有EXPLAIN,就没有多少工作了。还要记住,MySQL将查看您的JOIN,并在执行查询之前迭代所有可能的解决方案,这可能需要一些时间。从EXPLAIN获得最佳JOIN顺序后,您可以尝试在查询中强制执行此顺序,从而从优化程序中删除此步骤。

答案 6 :(得分:-1)

听起来你应该考虑提供子集(分页)或以其他方式限制结果,除非有一个原因是用户一次需要所有可能的行。通常100K行比普通人可以消化的行多。