如何加快多个连接的MySQL查询

问题描述:

这是我的问题,我正在选择并进行多个连接以获取正确的项目......它抽取了大量100,000行以上的行。当日期范围设置为1年时,此查询需要5分钟以上。如何加快多个连接的MySQL查询

我不知道这是否可能,但恐怕用户可能会将日期范围扩展到十年并使其崩溃。

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

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所以任何帮助将不胜感激!

在此先感谢!

UPDATE

这里是说明你要的

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 

而且我添加了一个索引表5行和表4行,因为他们没有真正改变,但其他表得到解决每月500-1000个条目......我听说你应该在一张有很多新条目的表格中添加一个索引......这是真的吗?

+3

这将有助于了解表格和索引 - 请更新以包含CREATE TABLE语句。对“DISTINCT”的需求让我想知道哪些表需要负责。 – 2010-10-21 16:17:25

+0

解释也会有帮助。 – ceejayoz 2010-10-21 16:33:52

+0

-OMG小马,我不知道你的意思是由创建表语句。 -Ceejayoz,我添加了EXPLAIN – BinarySolo00100 2010-10-22 19:11:50

我会尝试以下方法:

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

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 

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

在任何情况下,都会搅乱它。我的意思是,最终它只是一个选择 - 你不能真正伤害任何东西。检查每个不同排列产生的计划,并试图找出每个排列的好坏。

分享和享受。

+0

谢谢,我认为这会有所帮助,我会乱七八糟的。我对MySQL或数据库内容不是很了解,所以我非常感谢你的帮助。另外,我更新了这个问题,我没有为某些表添加索引,因为我不确定是否应该这样做,因为每个月都会增加约500-1000个新行。 – BinarySolo00100 2010-10-22 19:14:13

+1

我结束了使用这个,它造成了巨大的巨大影响,谢谢! – BinarySolo00100 2010-10-22 23:27:53

+1

@ BinarySolo00100 - 很高兴听到它的帮助。关于添加索引 - 我猜你担心索引开销可能会降低插入和更新速度。我的经验是,由于某些原因,人们(特别是DBA)太担心索引开销,并因此导致SELECT性能下降。我认为这是一个过早优化的例子,即担心一个不存在的潜在问题。直到你有一个可衡量的问题,你没有问题。添加索引(或其他)和MEASURE,MEASURE,MEASURE - 然后根据这些信息采取行动。 – 2010-10-23 00:01:36

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

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

另外,使用DISTINCT可以为优化器在幕后运行的逻辑添加额外的比较。如果可能的话消除。

听起来像你应该考虑提供子集(分页)或以其他方式限制结果,除非有用户需要同时需要每行的可能性。通常100K行比普通人更容易消化。

+1

这完全有可能用于导出或被应用程序层使用。我不知道有很多人逐行读取SQL查询结果。 – JNK 2010-10-21 16:25:18

+0

我不太清楚为什么当我明确表示'除非有一个原因,用户需要一次可能的所有行',否则我已经被低估了。 – 2010-10-21 17:39:42

嗯,首先,做一个子查询抽取表1下降到只有你真正想去参加的所有麻烦的记录...

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子查询,然后加入它背对背。

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

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

大家都说,确保你有索引。

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

没有解释,没有太多的工作。另外请记住,MySQL将查看您的JOIN,并在执行查询之前遍历所有可能的解决方案,这可能需要一些时间。一旦您从EXPLAIN获得了最佳JOIN顺序,您可以尝试在查询中强制执行此顺序,从优化程序中消除此步骤。

目前,您的查询正在返回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);