查询优化/修复

时间:2016-12-29 07:16:42

标签: mysql sql query-optimization

我前一周有一个类似的问题,一个查询需要永远运行。在编写此查询时,我尝试应用从其他查询中学到的一些东西,但是执行它需要很长时间。

当运行查询的两个单独部分时,每个部分需要2分钟才能完成,这是可以接受的,但速度会更快。

这是我的查询:

SELECT
    x.entityCode    'Entity Code'
,   x.nnd_volume    'Latest Brand Volume'
,   maxDate         'Last Action Date'
,   x.Brand         'Brand Description'
,   (SELECT 
            ROUND(((SELECT (MAX(A.line_value * B.pack_volume)) 
                         FROM master_data.brand_master B 
                         WHERE A.brand_code = B.brand_code
                           AND A.brand_code = B.brand_code) 
                  / --THIS IS NOT A BUG, JUST TRYING TO CLEARLY INDICATE THE TWO SELECTS BEING DIVIDED AND CALCULATED
                   (SELECT C.case_volume 
                         FROM master_data.brand_master C 
                         WHERE A.brand_code = C.brand_code
                           AND A.brand_code = C.brand_code)),2) 'brand_volume' 
       FROM am_lines.brand_module A
       WHERE A.action_date BETWEEN DATE(DATE_SUB(CURDATE(), INTERVAL 28 DAY))
         AND DATE(DATE_SUB(CURDATE(), INTERVAL 0 DAY))
         AND A.entity_code = x.entityCode
         AND A.brand_code = x.brandCode
       GROUP BY A.entity_code, A.brand
    ) 'Max Brand (28 Days)'
FROM
    (SELECT
        BBM.entity_code AS entityCode    
    ,   MAX(action_date) AS maxDate
    ,   brand AS Brand
    ,   brand_code AS brandCode
    ,   ROUND(((SELECT (BBM.line_value * B.pack_volume) 
        FROM master_data.brand_master B 
        WHERE BBM.brand_code = B.brand_code AND BBM.brand_code = B.brand_code) 
    / --THIS IS NOT A BUG, JUST TRYING TO CLEARLY INDICATE THE TWO SELECTS BEING DIVIDED AND CALCULATED
    (SELECT C.box_volume 
        FROM master_data.brand_master C 
        WHERE BBM.brand_code = C.brand_code AND BBM.brand_code = C.brand_code)),2) 'brand_volume' 
    FROM 
        am_lines.brand_module BBM
    WHERE 
        line_field_id IN (3154, 3655)
    AND action_date BETWEEN DATE(DATE_SUB(CURDATE(), INTERVAL 28 DAY)) AND DATE(DATE_SUB(CURDATE(), INTERVAL 0 DAY))    
    GROUP BY
    --  action_date
        entity_code
    ,   brand_code) AS X
GROUP BY
    x.entityCode
,   x.brand;    

样品数据(包装体积和盒体积只是一个参考表,表示包装/盒子中的总单位,用于计算,因此我没有将其包含在样品数据中):

    entity_code | action_date| brand    | line_value
    ------------+------------+----------+----------- 
    108792      |2016-12-07  |brand 001 | 25
    108793      |2016-12-08  |brand 002 | 36
    108795      |2016-12-06  |brand 003 | 10
    108796      |2016-12-05  |brand 004 | 55
    108795      |2016-12-13  |brand 001 |  5
    108792      |2016-12-14  |brand 003 |  2 
    108793      |2016-12-14  |brand 005 | 15 
    108796      |2016-12-16  |brand 006 | 25

目前的成效:

  • 每个选择查询在几分钟内运行(最多2个)并给出 期望的结果。
  • 将action_date和line_field_id编入索引。
  • 为entity_code添加一个索引会因为一些未知原因而缩短表格。

当前遇到的问题/期望的结果:

  • 整个查询运行时需要永远运行,超过2 小时。
  • 每个实体将出售/持有多个品牌的股票。我需要确定28天周期和一个月内的最大库存量(最大持有量28天列在期望值中)。
  • 我还需要显示最近一周的股票持有量(Brand Holding Column In desired set)。

期望的结果集视图:

    entity_code | Last Date  | brand holding    | Max Holding (28 Days) |Brand
    ------------+------------+------------------+-----------------------+----------+
    108792      |2016-12-27  |10                | 25                    |Brand 001
    108792      |2016-12-27  |36                | 36                    |Brand 002
    108792      |2016-12-27  |5                 | 10                    |Brand 003
    108792      |2016-12-27  |25                | 55                    |Brand 004
    108792      |2016-12-27  |4                 |  5                    |Brand 005
    108783      |2016-12-15  |80                | 80                    |Brand 001
    108783      |2016-12-15  |36                | 41                    |Brand 002
    108783      |2016-12-15  |12                | 12                    |Brand 003
    108783      |2016-12-15  |8                 | 11                    |Brand 004
    108783      |2016-12-15  |20                | 90                    |Brand 005        

QUERY PLAN(store_code与实体代码相同,我只是为了帖子而重命名): QUERY PLAN (store_code is the same as entity code, I just renamed it for the purpose of the post):

我已将我的索引调整为action_date和line_field_id。

SHOW CREATE TABLE图片: enter image description here

1 个答案:

答案 0 :(得分:0)

让我给你一些提示。如果它们还不够,那就重新开始吧。

  • 您不需要许多子查询;你会更好地使用JOINs
  • CURDATE没有时间,因此无需转换为DATEDATE(DATE_SUB(CURDATE(), INTERVAL 28 DAY)) - > (CURDATE() - INTERVAL 28 DAY)
  • 请使用某种缩进来帮助区分子查询。
  • 什么保证(SELECT ROUND...只返回一行?也许GROUP BY是多余的?
  • 有时您使用brand,有时使用brand_code;这是一个'错误'吗?
  • 添加以下索引;我不知道哪些是最好的。

索引:

INDEX(action_date, entity_code, brand_code)
INDEX(line_field_id, action_date)
INDEX(entity_code, brand, action_date)

<强>附加物

  • 请勿将VARCHAR用于需要计算的值,例如MAX(A.line_value * B.pack_volume)
  • 请勿在{{1​​}}上盲目使用(255),使用'合理'的最大长度。
  • 在任何InnoDB表上都有VARCHAR
  • 不匹配的类型,例如PRIMARY KEYline_field_id IN (3154, 3655)会妨碍合理的优化,而可能会产生“错误的”结果。

(在至少数据类型被“修复”之前,我不会再考虑这个问题。)