【问题标题】:mysql subquery extremely slowmysql子查询非常慢
【发布时间】:2013-09-28 17:09:54
【问题描述】:

我展示了一个涉及多个跟踪表的销售查询。 (仅显示部分数据)

SELECT  
    CONCAT(cam.c_prefix, LPAD(v.id_sale,5,'0')) Code,
    ve.estate_name last_state,
    v.date_instalation, 
    va.date_state date_modification  
FROM sale v 
INNER JOIN headquarters_detail sd ON v.id_headquarters_detail = sd.id_headquarters_detail 
INNER JOIN headquarters se ON sd.id_headquarters = se.id_headquarters 
INNER JOIN campaign cam ON sd.id_campaign = cam.id_campaign 
INNER JOIN sale_activity va ON va.id_sale = v.id_sale 
INNER JOIN state_detail vec ON vec.id_state_detail = va.id_state_detail
INNER JOIN sale_state ve ON ve.iid_sale_state = vec.iid_sale_state
WHERE  v.flag = 1 
AND     EXISTS( SELECT  '1' 
                            FROM sale_history
                            WHERE   id_sale = v.id_sale 
                            AND flag = 1
                            LIMIT 1)  
AND v.id_headquarters_detail = 2 

此查询需要 0.650 秒。我想在不同的列中添加子查询,我返回按阶段分配的销售的最后状态。比如:

id_sale | ... | last_state_of_stage_1 | last_state_of_stage_2 | last_state_of_stage_3
01      | ... | new_sale              | distributed_sale      | canceled_sale
02      | ... | new_sale              | distributed_sale      | invoiced_sale
...

每次销售的状态和阶段都存储在历史记录中:

sale_history
------------
id_history  int  -- primary key
id_sale int
id_state_detail int -- contains the states id's and logic
observation tinytext
date_history    datetime
id_user int
id_profile  int
...

索引:

CREATE INDEX indx_id_sale USING BTREE ON sale_history (id_sale);
CREATE INDEX indx_id_state_detail USING BTREE ON sale_history (id_state_detail);

每个历史记录平均有 25 条待售记录。为了获得销售状态,我想一个子查询(以及每个阶段):

SELECT  
    CONCAT(cam.c_prefix, LPAD(v.id_sale,5,'0')) Code,
    ve.estate_name last_state,
    v.date_instalation, 
    va.date_state date_modification,  

IFNULL((SELECT state_name
            FROM sale_history veh 
            INNER JOIN state_detail vec ON  vec.id_state_detail = veh.id_state_detail 
            INNER JOIN sale_state ve ON ve.iid_sale_state= vec.iid_sale_state
            WHERE veh.id_sale = v.id_sale
            AND vec.iid_sale_stage = 5
            AND veh.flag = 1  
            ORDER BY veh.id_history DESC
            LIMIT 1
),'x') last_state_of_stage_1

FROM sale v 
INNER JOIN headquarters_detail sd ON v.id_headquarters_detail = sd.id_headquarters_detail 
INNER JOIN headquarters se ON sd.id_headquarters = se.id_headquarters 
INNER JOIN campaign cam ON sd.id_campaign = cam.id_campaign 
INNER JOIN sale_activity va ON va.id_sale = v.id_sale 
INNER JOIN state_detail vec ON vec.id_state_detail = va.id_state_detail
INNER JOIN sale_state ve ON ve.iid_sale_state = vec.iid_sale_state
WHERE  v.flag = 1 
AND     EXISTS( SELECT  '1' 
                            FROM sale_history
                            WHERE   id_sale = v.id_sale 
                            AND flag = 1
                            LIMIT 1)  
AND v.id_headquarters_detail = 2

但是这个子查询 115.985s 延迟!我想知道是因为它需要这么长时间吗?

编辑 30-09-13

通过fancyPants的cmets,我做了一些改动,大大提高了查询的速度:

SELECT  
    CONCAT(cam.c_prefix, LPAD(v.id_sale,5,'0')) Code,
    ve.estate_name last_state,
    v.date_instalation, 
    va.date_state date_modification,
    IFNULL(ve5.state_name,'x') last_state_of_stage_1
FROM sale v 
INNER JOIN headquarters_detail sd ON v.id_headquarters_detail = sd.id_headquarters_detail 
INNER JOIN headquarters se ON sd.id_headquarters = se.id_headquarters 
INNER JOIN campaign cam ON sd.id_campaign = cam.id_campaign 
INNER JOIN sale_activity va ON va.id_sale = v.id_sale 
INNER JOIN state_detail vec ON vec.id_state_detail = va.id_state_detail
INNER JOIN sale_state ve ON ve.iid_sale_state = vec.iid_sale_state
LEFT JOIN sale_history veh5 ON veh5.id_sale = v.id_sale AND veh5.flag = 1 AND veh5.id_history = (SELECT MAX(sh.id_history) 
                                                                                                                                                                                                    FROM sale_history sh 
                                                                                                                                                                                                    INNER JOIN state_detail vecx ON vecx.id_state_detail = sh.id_state_detail
                                                                                                                                                                                                    INNER JOIN sale_state vex ON vex.iid_sale_state= vecx.iid_sale_state
                                                                                                                                                                                                    WHERE sh.id_sale = v.id_sale AND sh.id_sale = veh5.id_sale
                                                                                                                                                                                                    AND sh.flag = 1
                                                                                                                                                                                                    AND vecx.iid_sale_stage = 5
                                                                                                                                                                                                    )
LEFT JOIN state_detail vec5 ON vec5.id_state_detail = veh5.id_state_detail
LEFT JOIN sale_state ve5 ON ve5.iid_sale_state = vec5.iid_sale_state
WHERE  v.flag = 1 
AND     EXISTS( SELECT  '1' 
                            FROM sale_history
                            WHERE   id_sale = v.id_sale 
                            AND flag = 1
                            LIMIT 1)  
AND v.id_headquarters_detail = 2 
AND EXISTS(SELECT '1'
                FROM sale_activity veh
                INNER JOIN state_detail vec ON  vec.id_state_detail = veh.id_state_detail 
                INNER JOIN sale_state ve    ON ve.iid_sale_state= vec.iid_sale_state
                WHERE id_sale = v.id_sale
                AND iid_sale_stage = 4
                LIMIT 1)

现在用 0.609s 显示数据,谢谢!

【问题讨论】:

    标签: mysql sql performance subquery


    【解决方案1】:

    如果此查询效果更好,请试一试:

    SELECT  
        CONCAT(cam.c_prefix, LPAD(v.id_sale,5,'0')) Code,
        ve.estate_name last_state,
        v.date_instalation, 
        va.date_state date_modification,  
        COALESCE(state_name, 'x') last_state_of_stage_1
    FROM sale v 
    INNER JOIN headquarters_detail sd ON v.id_headquarters_detail = sd.id_headquarters_detail 
    INNER JOIN headquarters se ON sd.id_headquarters = se.id_headquarters 
    INNER JOIN campaign cam ON sd.id_campaign = cam.id_campaign 
    INNER JOIN sale_activity va ON va.id_sale = v.id_sale 
    INNER JOIN state_detail vec ON vec.id_state_detail = va.id_state_detail
    INNER JOIN sale_state ve ON ve.iid_sale_state = vec.iid_sale_state
    INNER JOIN sale_history veh ON veh.id_sale = v.id_sale AND vec.iid_sale_stage = 5 AND veh.flag = 1 
    WHERE  v.flag = 1 
    AND v.id_headquarters_detail = 2
    AND veh.id_history = (SELECT MAX(id_history) FROM sale_history sh WHERE sh.id_sale = veh.id_sale);
    

    如果没有(假设它产生正确的结果),请在前面加上EXPLAIN 再次执行它并发布结果。

    【讨论】:

      【解决方案2】:

      内部查询会一遍又一遍地为表的每一行单独运行,从而使查询时间呈指数级增长(记录越多越糟糕)。这就是为什么在现实世界的应用程序中要小心处理子查询的原因。

      我建议您对该查询尝试不同的方法(在很多情况下,一些复杂的 JOIN 组合可以替换子查询)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-23
        • 1970-01-01
        • 2020-08-14
        • 2021-12-09
        • 1970-01-01
        相关资源
        最近更新 更多