优化MYSQL子查询

时间:2015-08-08 19:32:33

标签: mysql

无论如何都要优化此查询?

我知道它在子查询中被阻止了:

    (SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords
    WHERE store_id=@storeid AND upc=855555000032)

如果没有IFNULL子查询,它会在不到1秒的时间内执行。这个例子与subquerys相比需要20秒,而且只有54行。无论如何要改写它以加快它?感谢您的帮助。

SELECT 
   SQL_CALC_FOUND_ROWS @storeid:=z.id
  ,z.biz_name
  ,z.wf_store_name
  ,z.e_address
  ,z.e_city
  ,z.e_state 
  ,z.e_postal
  ,IFNULL(total_sales - prev_total_sales,'CV') as diff_total_sales
  ,IFNULL(d_source,'N/A') as d_source
  ,IFNULL(unit_sales1 - prev_unit_sales1 , (SELECT IFNULL(max(datetimesql),'NS') 
                                            FROM storeCheckRecords 
                                            WHERE store_id=@storeid AND upc=855555000032)
   ) as diff_unit_sales1
  ,IFNULL(unit_sales2 - prev_unit_sales2, (SELECT IFNULL(max(datetimesql),'NS') 
                                           FROM storeCheckRecords 
                                           WHERE store_id=@storeid AND upc=855555000033)
   ) as diff_unit_sales2
  ,IFNULL(unit_sales3 - prev_unit_sales3, (SELECT IFNULL(max(datetimesql),'NS') 
                                           FROM storeCheckRecords 
                                           WHERE store_id=@storeid AND upc=855555000034)
   ) as diff_unit_sales3
  ,IFNULL(unit_sales4 - prev_unit_sales4, (SELECT IFNULL(max(datetimesql),'NS') 
                                           FROM storeCheckRecords 
                                           WHERE store_id=@storeid AND upc=855555000035)
   ) as diff_unit_sales4
  ,IFNULL(unit_sales5 - prev_unit_sales5, (SELECT IFNULL(max(datetimesql),'NS') 
                                           FROM storeCheckRecords 
                                           WHERE store_id=@storeid AND upc=855555000036)
   ) as diff_unit_sales5
  ,IFNULL(unit_sales6 - prev_unit_sales6, (SELECT IFNULL(max(datetimesql),'NS') 
                                           FROM storeCheckRecords 
                                           WHERE store_id=@storeid AND upc=855555000038)
   ) as diff_unit_sales6 
FROM  
   (SELECT  s1.id,s1.biz_name as biz_name 
           ,s1.wf_store_name as wf_store_name
           ,s1.e_address as e_address
           ,s1.e_city as e_city
           ,s1.e_state as e_state
           ,s1.e_postal as e_postal
           ,sum(s2.unit_sales) as total_sales
           ,sum(s2.unit_sales/4.28571428571) as week_avg
           ,group_concat(DISTINCT s2.d_source separator ',') as d_source
           ,SUM(CASE u.id WHEN 1 THEN s2.unit_sales ELSE NULL END) AS unit_sales1
           ,SUM(CASE u.id WHEN 2 THEN s2.unit_sales ELSE NULL END) AS unit_sales2
           ,SUM(CASE u.id WHEN 3 THEN s2.unit_sales ELSE NULL END) AS unit_sales3
           ,SUM(CASE u.id WHEN 4 THEN s2.unit_sales ELSE NULL END) AS unit_sales4
           ,SUM(CASE u.id WHEN 5 THEN s2.unit_sales ELSE NULL END) AS unit_sales5
           ,SUM(CASE u.id WHEN 6 THEN s2.unit_sales ELSE NULL END) AS unit_sales6
    FROM allStores as s1
    INNER JOIN storeCheckRecords AS s2
      ON    s1.id = s2.store_id
        AND s2.datetimesql BETWEEN '2015-07-01' AND '2015-07-31'
        AND s1.key_retailer LIKE 'WHOLE FOODS' 
        AND s1.wf_region LIKE 'Midwest' 
    INNER JOIN (SELECT  1 AS id
                       ,'855555000032' AS upc 
                UNION 
                SELECT  2
                       ,'855555000033' 
                UNION 
                SELECT  3
                       ,'855555000034' 
                UNION 
                SELECT  4
                       ,'855555000035' 
                UNION 
                SELECT  5
                       ,'855555000036' 
                UNION 
                SELECT  6
                       ,'855555000038') u 
      ON u.upc = s2.upc  
    GROUP BY s1.id) x
LEFT OUTER JOIN
   (SELECT  s1.id,s1.biz_name as prev_biz_name
           ,s1.wf_store_name as prev_wf_store_name
           ,s1.e_address as prev_e_address
           ,s1.e_city as prev_e_city
           ,s1.e_state as prev_e_state
           ,s1.e_postal as prev_e_postal
           ,sum(s2.unit_sales) as prev_total_sales
           ,sum(s2.unit_sales/4.28571428571) as prev_week_avg
           ,group_concat(DISTINCT s2.d_source separator ',') as prev_d_source
           ,SUM(CASE u.id WHEN 1 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales1
           ,SUM(CASE u.id WHEN 2 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales2
           ,SUM(CASE u.id WHEN 3 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales3
           ,SUM(CASE u.id WHEN 4 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales4
           ,SUM(CASE u.id WHEN 5 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales5
           ,SUM(CASE u.id WHEN 6 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales6
    FROM allStores as s1
    INNER JOIN storeCheckRecords AS s2
      ON    s1.id = s2.store_id
        AND s2.datetimesql BETWEEN '2015-06-01' AND '2015-06-30'
        AND s1.key_retailer LIKE 'WHOLE FOODS' 
        AND s1.wf_region LIKE 'Midwest'
    INNER JOIN (SELECT  1 AS id
                       ,'855555000032' AS upc 
                UNION 
                SELECT  2
                       ,'855555000033' 
                UNION 
                SELECT  3
                       ,'855555000034' 
                UNION 
                SELECT  4
                       ,'855555000035' 
                UNION 
                SELECT  5
                       ,'855555000036' 
                UNION 
                SELECT  6
                       ,'855555000038') u 
      ON u.upc = s2.upc  
    GROUP BY s1.id) y
ON x.id = y.id
RIGHT JOIN
   (SELECT  s1.id
           ,s1.biz_name
           ,s1.wf_store_name
           ,s1.e_address
           ,s1.e_city
           ,s1.e_state
           ,s1.e_postal
    FROM allStores as s1
    WHERE     1 
          AND s1.key_retailer LIKE 'WHOLE FOODS' 
          AND s1.wf_region LIKE 'Midwest') z
ON y.id = z.id
ORDER BY wf_store_name ASC
LIMIT 0, 1000

谢谢亨利。我索引了upc,store_id和datetimsql。这减少了一半,但我想进一步降低它。这是解释,我试图在sql中完成所有事情,因为我将结果提供给数据表。

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   PRIMARY <derived22> ALL NULL    NULL    NULL    NULL    54  Using temporary; Using filesort
1   PRIMARY <derived8>  ALL NULL    NULL    NULL    NULL    6   
1   PRIMARY <derived15> ALL NULL    NULL    NULL    NULL    6   
22  DERIVED s1  ALL NULL    NULL    NULL    NULL    64121   Using where
15  DERIVED <derived16> ALL NULL    NULL    NULL    NULL    6   Using temporary; Using filesort
15  DERIVED s2  ref upc,store_id,upc_2,store_id_2,datetimesql,datetime...   upc 47  u.upc   2159    Using where
15  DERIVED s1  eq_ref  PRIMARY PRIMARY 4   dpalame_foodiecpg.s2.store_id   1   Using where
16  DERIVED NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
17  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
18  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
19  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
20  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
21  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
NULL    UNION RESULT    <union16,17,18,19,20,21>    ALL NULL    NULL    NULL    NULL    NULL    
8   DERIVED <derived9>  ALL NULL    NULL    NULL    NULL    6   Using temporary; Using filesort
8   DERIVED s2  range   upc,store_id,upc_2,store_id_2,datetimesql,datetime...   datetimesql 3   NULL    1810    Using where; Using join buffer
8   DERIVED s1  eq_ref  PRIMARY PRIMARY 4   dpalame_foodiecpg.s2.store_id   1   Using where
9   DERIVED NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
10  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
11  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
12  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
13  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
14  UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
NULL    UNION RESULT    <union9,10,11,12,13,14> ALL NULL    NULL    NULL    NULL    NULL    
7   UNCACHEABLE SUBQUERY    storeCheckRecords   index   upc,upc_2   datetimesql_3   53  NULL    60452   Using where; Using index
6   UNCACHEABLE SUBQUERY    storeCheckRecords   index   upc,upc_2   datetimesql_3   53  NULL    60452   Using where; Using index
5   UNCACHEABLE SUBQUERY    storeCheckRecords   index   upc,upc_2   datetimesql_3   53  NULL    60452   Using where; Using index
4   UNCACHEABLE SUBQUERY    storeCheckRecords   index   upc,upc_2   datetimesql_3   53  NULL    60452   Using where; Using index
3   UNCACHEABLE SUBQUERY    storeCheckRecords   index   upc,upc_2   datetimesql_3   53  NULL    60452   Using where; Using index
2   UNCACHEABLE SUBQUERY    storeCheckRecords   index   upc,upc_2   datetimesql_3   53  NULL    60452   Using where; Using index

1 个答案:

答案 0 :(得分:1)

首先尝试使用内置查询分析器的mysql

http://dev.mysql.com/doc/refman/5.0/en/explain-extended.html

 explain [your_query]

然后回来结果。 我的猜测是查询的这一部分

INNER JOIN (SELECT  1 AS id
                   ,'855555000032' AS upc 
            UNION 
            SELECT  2
                   ,'855555000033' 
            UNION 
            SELECT  3
                   ,'855555000034' 
            UNION 
            SELECT  4
                   ,'855555000035' 
            UNION 
            SELECT  5
                   ,'855555000036' 
            UNION 
            SELECT  6
                   ,'855555000038') u 

如果这个子查询是静态的,那么应该更好地创建一个myisam表,并为列id和列upc创建索引

您的查询工作方式,将使用很多临时表。 对于每个子查询,mysql都会创建一个临时表。 如果查询的结果足够大,性能将会降低。

https://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html

您可以将查询重新设计为单独的视图

另外,慢查询可能是由unindex参数列(在查询中使用的列)

引起的

一个方便的性能方法是为针对表使用的查询的每个“where”参数创建一个set column(s)索引,为每个“order”设置一个列的索引

尝试查看表“storeCheckRecords”中的“storeId”和“upc”列是否已作为集合编入索引。

还要查看你的mysql配置中是否设置了“innodb_file_per_table = 1”。 单独的表空间对涉及大表数据的性能有积极影响。 但是,如果之前innodb_file_per_table设置为0,则需要重新创建整个数据库。