2015-08-27 76 views
2

我有这个疑问如何改善这个表

EXPLAIN SELECT 
    GA, 
    mkt_cd, 
    mkt_name 
FROM 
    (SELECT 
    SUM(GA) AS GA, 
    sales_data_mkt.mkt_cd, 
    sales_data_mkt.mkt_name 
    FROM 
    sales_data_mkt 
    WHERE sales_data_mkt.country_cd = 'USA' 
    AND activity_dt BETWEEN '2015-08-01' 
    AND '2015-08-31' 
    AND sales_data_mkt.mkt_cd IS NOT NULL 
    GROUP BY sales_data_mkt.mkt_cd 
    ORDER BY (SUM(GA)) DESC) AS innerQuery 
WHERE GA > 0 
LIMIT 10 

我有一个索引 country_cd, activity_dt, mkt_cd

EXPLAIN语句返回此指数:

Key used:country_cd,activity_dt,mkt_cd

key_len: ref:常量

rows:

Extra:使用索引条件;在哪里使用;使用临时;使用filesort

此查询大约需要5秒才能返回一个包含200万行的表。从我过去的阅读Using temporary and Using filesort是不好的表现。我怎样才能微调这个查询?


这里是create语句

CREATE TABLE `sales_data_mkt` (
    `ACTIVITY_DT` date DEFAULT NULL, 
    `Country_Cd` varchar(3) DEFAULT NULL, 
    `AREA_CD` char(2) DEFAULT NULL, 
    `AREA_DESC` varchar(30) DEFAULT NULL, 
    `REGION_CD` char(2) DEFAULT NULL, 
    `REGION_DESC` varchar(30) DEFAULT NULL, 
    `MKT_CD` char(4) DEFAULT NULL, 
    `MKT_NAME` varchar(30) DEFAULT NULL, 
    `device_tier` varchar(32) DEFAULT NULL, 
    `SLS_DIST_CHNL_TYPE_CD` char(3) DEFAULT NULL, 
    `PPlan_Type` varchar(14) DEFAULT NULL, 
    `PREPAID_IND` char(1) DEFAULT NULL, 
    `edge_taken_ind` varchar(1) DEFAULT NULL, 
    `Edge_Desc` varchar(16) DEFAULT NULL, 
    `Data_Plan_Tier` varchar(26) DEFAULT NULL, 
    `Unlimited_to_Others_cnt` int(11) DEFAULT NULL, 
    `Data_Step_UP_Cnt` int(11) DEFAULT NULL, 
    `Data_Step_Down_Cnt` int(11) DEFAULT NULL, 
    `lines` int(11) DEFAULT NULL, 
    `GA` int(11) DEFAULT NULL, 
    `DE` int(11) DEFAULT NULL, 
    `NetAdd` int(11) DEFAULT NULL, 
    `VOL_DE` int(11) DEFAULT NULL, 
    `INVOL_DE` int(11) DEFAULT NULL, 
    `PortIn_ATT_Leap` int(11) DEFAULT NULL, 
    `PortIn_Sprint_Nextel` int(11) DEFAULT NULL, 
    `PortIn_TMobile_MetroPcs` int(11) DEFAULT NULL, 
    `PortIn_OtherCarriers` int(11) DEFAULT NULL, 
    `PortOut_ATT_Leap` int(11) DEFAULT NULL, 
    `PortOut_Sprint_Nextel` int(11) DEFAULT NULL, 
    `PortOut_TMobile_MetroPcs` int(11) DEFAULT NULL, 
    `PortOut_OtherCarriers` int(11) DEFAULT NULL, 
    `Edge_Net_Sales` int(11) DEFAULT NULL, 
    `Edge_Eligible_Net_Sales` int(11) DEFAULT NULL, 
    `Edge_Net_Sales_All` int(11) DEFAULT NULL, 
    `Basic_To_Smart` int(11) DEFAULT NULL, 
    `AAL` int(11) DEFAULT NULL, 
    `New_To_VZ` int(11) DEFAULT NULL, 
    `Trade_In` int(11) DEFAULT NULL, 
    `Unlimited_to_Others` int(11) DEFAULT NULL, 
    `Data_Step_Up` int(11) DEFAULT NULL, 
    `Data_Step_Down` int(11) DEFAULT NULL, 
    KEY `MKT_CD` (`MKT_CD`,`ACTIVITY_DT`), 
    KEY `REGION_CD` (`REGION_CD`,`ACTIVITY_DT`,`MKT_CD`), 
    KEY `AREA_CD` (`AREA_CD`,`ACTIVITY_DT`), 
    KEY `Country_Cd` (`Country_Cd`,`ACTIVITY_DT`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
+0

什么是你的创建表,所以我可以匹配它。运行'show create table sales_data_mkt'并发布 – Drew

+0

@Drew添加了创建语句 – codeNinja

回答

0

你不应该需要一个子查询,确实这个版本的帮助?

SELECT 
    SUM(GA) AS TotalGA, 
    sales_data_mkt.mkt_cd, 
    sales_data_mkt.mkt_name 
    FROM 
    sales_data_mkt 
    WHERE sales_data_mkt.country_cd = 'USA' 
    AND activity_dt BETWEEN '2015-08-01' AND '2015-08-31' 
    AND sales_data_mkt.mkt_cd IS NOT NULL 
    GROUP BY sales_data_mkt.mkt_cd 
    HAVING TotalGA > 0 
    ORDER BY TotalGA DESC 
    LIMIT 10 
    ; 

它可能工作即使没有重命名GATotalGA,那只是看起来错误和潜在的问题给我。

旁注:如果activity_dt是DATETIME或TIMESTAMP,那么BETWEEN可能无法正常工作。在建立一个 “复合” 索引

+0

这个查询没有什么区别。解释说明和运行时间是相同的。 (activity_dt是DATE) – codeNinja

0

教程#1:= constant列的第一,然后一个 '范围' 列中。

WHERE country_cd = 'USA' 
    AND activity_dt BETWEEN '2015-08-01' AND '2015-08-31' 
    AND mkt_cd IS NOT NULL 

所以,需要以下任一:

INDEX(country_cd, mkt_cd, activity_dt) 
INDEX(mkt_cd, country_cd, activity_dt) 

它说Using index表明该查询在索引完全完成。

key_len: 12 ref: const 

需要一些解码。它说const,而不是const,const,所以它只使用1个字段。它使用activity_dt的范围检查(不包括其在12,但它绊倒了错误的值mkt_cd,使之更慢。

12来自Country_Cd varchar(3) DEFAULT NULL。但是如何,你问?key_len是最大的,所以3个字符。因为它是VAR,所以需要2个字节的长度。另一个字节为NULL。由于utf8,每个字符是3个字节。所以,3 * 3 + 2 + 1 = 12个字节。

  • 提示:如果列中始终有一个值,则说NOT NULL
  • 提示:在总是ascii的列(如“国家代码”)上明确指出CHARACTER SET ascii
  • 提示:由于国家代码标准化为3个字母,因此不需要VAR。 (我假设你使用的是3字母标准,而不是2字母标准)。

按照提示和key_len将从12降到1 * 3 + 0 + 0 = 3个字节。

  • 提示:使用LIMIT而不使用ORDER BY可让优化器为您提供任何感觉的行。只要看到几行就行,但对于生产来说,这是危险的不可预测的。 (不,优化器可以随意地忽略内部ORDER BY。)
  • 提示:通过使用HAVING,你并不需要一个子查询(如Uueerdo说):

    SELECT SUM(GA) AS GA, mkt_cd, mkt_name FROM sales_data_mkt WHERE country_cd = 'USA' AND activity_dt BETWEEN '2015-08-01' AND '2015-08-31' AND mkt_cd IS NOT NULL GROUP BY mkt_cd HAVING SUM(GA) > 0 ORDER BY SUM(GA) DESC LIMIT 10 ;

  • 提示:InnoDB的真的需要一个PRIMARY KEY。如果您没有列数为UNIQUE的组合,请添加INT UNSIGNED NOT NULL AUTO_INCREMENT

  • 提示:为避免计算结束月份,闰年和失踪整天,改变activity_dt BETWEEN '2015-08-01' AND '2015-08-31'

    activity_dt >= '2015-08-01' AND
    activity_dt < '2015-08-01' + INTERVAL 1 MONTH

这将正常工作的DATEDATETIMETIMESTAMP,甚至是新的微秒精度数据类型。