我在为生产提高查询速度方面遇到了一些麻烦。
我想要执行的查询当前需要12秒才能显示结果集,并且它会崩溃受资源限制的生产服务器。
重点是,当它们是给定enregistrement
的最后一个(日期为YYYYMM)时,我需要获取所有periode
条记录。
获取这些记录后,我想将I.sum_field
中的一个字段作为total
字段汇总。
当我评论CASE部分时,查询大约需要5秒(+/- 500毫秒)。
以下是查询:
SELECT
I.libelle,
E1.periode,
E1.created_at,
CASE WHEN I.sum_field = 'fat' THEN SUM(E1.Fat)
WHEN I.sum_field = 'etp' THEN SUM(E1.Etp)
WHEN I.sum_field = 'nb_ident' THEN COUNT(*)
WHEN I.sum_field = 'cdi_actif' THEN SUM(E1.cdi_actif)
END AS total
FROM
indicateur_motif IM
INNER JOIN indicateur I
ON IM.indicateur_id = I.id
INNER JOIN `position` P
ON IM.motif_id = P.id
INNER JOIN enregistrement E1
ON P.id = E1.position_id
INNER JOIN
( SELECT
MAX(id) AS id,
MAX(created_at) AS created_at
FROM
enregistrement
WHERE
(etat_mouvement_id IN (1,3,4))
AND (periode >= '201410' AND periode <= '201512')
AND created_at <= DATE_FORMAT('2015-02-03', '%Y-%m-%d %H:%i:%s')
GROUP BY
salarie_id,
periode ) E2
ON E1.id = E2.id
AND E1.created_at = E2.created_at
WHERE
I.formule_id = 1
GROUP BY
I.id,
E1.periode
ORDER BY
I.position,
E1.periode
以下是EXPLAIN
结果:
id select_type table type possible_keys key key_len ref rows Extra
------ ----------- -------------- ------ ---------------------------------------------- ---------------------------------------------- ------- ------------------ ------ ----------------------------------------------------
1 PRIMARY I ALL PRIMARY (NULL) (NULL) (NULL) 21 Using where; Using temporary; Using filesort
1 PRIMARY IM ref indicateur_motif_indicateur_id_motif_id_unique indicateur_motif_indicateur_id_motif_id_unique 4 orhase.I.id 2 Using index
1 PRIMARY P eq_ref PRIMARY PRIMARY 4 orhase.IM.motif_id 1 Using index
1 PRIMARY <derived2> ALL (NULL) (NULL) (NULL) (NULL) 165352 Using where; Using join buffer (Block Nested Loop)
1 PRIMARY e1 eq_ref PRIMARY PRIMARY 4 e2.id 1 Using where
2 DERIVED enregistrement index sp sp 771 (NULL) 165352 Using where
以下是结果集的示例:
libelle periode created_at total
------------------------------------------ ------- ------------------- ---------
CDI actifs fin de période 201410 2014-10-01 00:00:00 4689
CDI actifs fin de période 201411 2015-01-29 08:12:03 4674
CDI actifs fin de période 201412 2015-01-29 08:12:03 4660
CDI actifs fin de période 201501 2015-01-29 08:12:04 4444
CDI actifs fin de période 201502 2015-01-29 08:12:04 4222
CDI actifs fin de période 201503 2015-01-29 08:12:04 4195
CDI actifs fin de période 201504 2015-01-29 08:12:04 4176
CDI actifs fin de période 201505 2015-01-29 08:12:04 4155
CDI actifs fin de période 201506 2015-01-29 08:12:04 4136
CDI actifs fin de période 201507 2015-01-29 08:12:04 4121
CDI actifs fin de période 201508 2015-01-29 08:12:04 4080
CDI actifs fin de période 201509 2015-01-29 08:12:04 4061
CDI actifs fin de période 201510 2015-01-29 08:12:04 4036
CDI actifs fin de période 201511 2015-01-29 08:12:04 4001
CDI actifs fin de période 201512 2015-01-29 08:12:04 3976
ETP fin de période CDI stock 201410 2014-10-01 00:00:00 4259.16
ETP fin de période CDI stock 201411 2015-01-29 08:12:03 4241.91
ETP fin de période CDI stock 201412 2015-01-29 08:12:03 4222.12
ETP fin de période CDI stock 201501 2015-01-29 08:12:04 4028.07
我根本不知道在哪里放置一个新的索引以避免这个执行时间...我已经在enregistrement
上放了一个,名为sp
:
ALTER TABLE enregistrement ADD INDEX sp(salarie_id, periode);
这个让我获得从16秒到12秒的执行时间。 有什么想法吗?
感谢。
答案 0 :(得分:0)
不知道这是否会有所帮助,但你的情况是做什么......你总结了完全不同的领域,并将另一个领域计算为“总计”。我怀疑你可能真的希望这些作为他们自己的专栏。
然而,话虽如此,你对索引有什么...你的解释显示了一些,但如果它们不可用,我会尝试包括以下内容......
table index
indicateur ( formule_id, id, position )
indicateur_motif ( indicateur_id, motif_id )
`position` ( id )
enregistrement ( position_id, id, created_at ) <-- for the JOIN portion
enregistrement ( etat_mouvement_id, periode, created_at, salarie_id, id ) <-- for sub-select query
此外,从您的联接中,您实际上并未使用“位置”表中的任何内容。是的,你从主题到位置,位置加入enreg,但是从
开始IM.motif_id = P.id and P.id = E1.position_id
然后你可以直接跳
IM.motif_id = E1.position_id
并从查询中删除'position'表。这是对您开始的内容的略微修改的查询。我删除了位置引用,并且还更改了内部查询的“分组依据”,以便它可以更好地匹配列periode和salarie_id的可用索引。
SELECT
I.libelle,
E1.periode,
E1.created_at,
CASE WHEN I.sum_field = 'fat' THEN SUM(E1.Fat)
WHEN I.sum_field = 'etp' THEN SUM(E1.Etp)
WHEN I.sum_field = 'nb_ident' THEN COUNT(*)
WHEN I.sum_field = 'cdi_actif' THEN SUM(E1.cdi_actif)
END AS total
FROM
indicateur I
JOIN indicateur_motif IM
ON I.id = IM.indicateur_id
INNER JOIN enregistrement E1
ON IM.motif_id = E1.position_id
INNER JOIN
( SELECT
MAX(id) AS id,
MAX(created_at) AS created_at
FROM
enregistrement
WHERE
etat_mouvement_id IN (1,3,4)
AND periode >= '201410'
AND periode <= '201512'
AND created_at <= '2015-02-03'
GROUP BY
periode,
salarie_id ) E2
ON E1.id = E2.id
AND E1.created_at = E2.created_at
WHERE
I.formule_id = 1
GROUP BY
I.id,
E1.periode
ORDER BY
I.position,
E1.periode
答案 1 :(得分:0)
我不知道你的表是什么样的,但是这个查询:
SELECT MAX(id) AS id, MAX(created_at) AS created_at
FROM enregistrement
WHERE (etat_mouvement_id IN (1,3,4))
AND (periode >= '201410' AND periode <= '201512')
AND created_at <= DATE_FORMAT('2015-02-03', '%Y-%m-%d %H:%i:%s')
GROUP BY salarie_id, periode
非常昂贵。如果您想尝试仅通过索引修复此问题,则向id
和created_at
列添加索引可能是一个良好的开端。我可能做的另一个建议是在单独的事务中运行此查询,并将结果插入临时表。这应该至少释放一些所需的资源,方法是将其转换为简单的连接,而不是在查询过程中进行非常复杂的搜索操作。如果这不起作用,您还可以尝试运行所有选择和连接而不加总和,将这些结果插入临时表,然后从那里选择和求和结果。
那就是说,没有看到你的表,每列中每个和所有数据的行数,你正在运行什么样的硬件,或者知道你的prod环境在使用方面是什么样的,它真的很难准确说出问题的确切位置。我非常确定MySQL中还没有内置函数,但是如果这对业务至关重要,那么使用Jet Profiler这样的查询分析可能是值得的。如果我正在编写一个崩溃生产服务器的查询,那么我将首先想要查看资源压力的确切位置。
答案 2 :(得分:0)
你的缓慢来自你对enregistrement的子选择。他们似乎都是表扫描看起来所有的记录。 IN也没有帮助。
尝试在下表字段中创建索引并告诉我。
enregistrement.etat_mouvement_id
enregistrement.periode
enregistrement.created_at
答案 3 :(得分:0)
在这里。我使用此查询将执行时间从12秒减少到6.8秒:
SELECT I.libelle, e1.periode,
CASE WHEN I.sum_field = 'fat' THEN SUM(E1.Fat)
WHEN I.sum_field = 'etp' THEN SUM(E1.Etp)
WHEN I.sum_field = 'nb_ident' THEN COUNT(*)
WHEN I.sum_field = 'cdi_actif' THEN SUM(E1.cdi_actif) END AS 'total'
FROM indicateur_motif IM
INNER JOIN indicateur I ON IM.indicateur_id = I.id
INNER JOIN enregistrement e1 ON IM.motif_id = e1.position_id
INNER JOIN
(
SELECT MAX(created_at) AS createdat, salarie_id, periode
FROM enregistrement
WHERE (etat_mouvement_id IN (1,3,4))
AND (periode >= '201410' AND periode <= '201512')
AND created_at <= DATE_FORMAT('2015-02-03', '%Y-%m-%d %H:%i:%s')
GROUP BY salarie_id, periode
) e2 ON (e1.created_at = e2.createdat AND e1.salarie_id = e2.salarie_id AND e1.periode = e2.periode)
WHERE I.formule_id = 1
GROUP BY I.id, e1.periode
ORDER BY I.position, e1.periode
仅供参考,此子查询:
SELECT MAX(created_at) AS createdat, salarie_id, periode
FROM enregistrement
WHERE (etat_mouvement_id IN (1,3,4))
AND (periode >= '201410' AND periode <= '201512')
AND created_at <= DATE_FORMAT('2015-02-03', '%Y-%m-%d %H:%i:%s')
GROUP BY salarie_id, periode
由于我的sp
索引:
ALTER TABLE enregistrement ADD INDEX sp(salarie_id, periode);
@DRapp :我在JOINS上是对的,我从联接中删除了position
并更正了查询。在总字段上,我确实希望在单个列上获取值,以便不对我的代码逻辑执行条件。
我尝试了 @DRapp 索引和查询提议,他们只是放慢速度或者没有改变我的查询。
id select_type table type possible_keys key key_len ref rows Extra
------ ----------- -------------- ------ ---------------------------------------------- ---------------------------------------------- ------- --------------------------------- ------ ----------------------------------------------------
1 PRIMARY <derived2> ALL (NULL) (NULL) (NULL) (NULL) 165352 Using temporary; Using filesort
1 PRIMARY e1 ref sp sp 771 e2.salarie_id,e2.periode 1 Using where
1 PRIMARY I ALL PRIMARY (NULL) (NULL) (NULL) 21 Using where; Using join buffer (Block Nested Loop)
1 PRIMARY IM eq_ref indicateur_motif_indicateur_id_motif_id_unique indicateur_motif_indicateur_id_motif_id_unique 8 orhase.I.id,orhase.e1.position_id 1 Using index
2 DERIVED enregistrement index sp sp 771 (NULL) 165352 Using where
使用此EXPLAIN结果,我想解析描述Using temporary; Using filesort
的第一行。解决方案是索引GROUP BY列,但我不知道是否可以在这两个字段上创建复合索引,因为它们来自不同的表。什么是更好的或替代解决方案?
感谢大家的回答:)