这是我的问题,我正在选择并进行多次连接以获取正确的项目...它会提取相当数量的行,超过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个条目...我听说你应该为一个有这个表的表添加一个索引许多新条目....这是真的吗?
答案 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行比普通人可以消化的行多。