我有这个查询
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:
12
ref:
const
rows:
909518
Extra:
使用索引条件;用在哪里;使用临时;使用filesort
此查询大约需要5秒才能返回包含200万行的表。从我过去的阅读Using temporary and Using filesort
来看,表现不佳。我该如何微调这个查询呢?
这是创建声明
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 :(得分: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
;
即使没有将GA
重命名为TotalGA
,它也可能有用,这看起来很错误,对我来说可能有问题。
旁注:如果activity_dt是DATETIME或TIMESTAMP,那么BETWEEN可能无法完全按预期工作。
答案 1 :(得分:0)
构建"复合材料" index:首先放置= 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
。CHARACTER SET ascii
,例如"国家/地区代码"。VAR
。 (我假设您使用的是3个字母的标准,而不是2个字母的标准。)按照提示操作,key_len
将从12降至1 * 3 + 0 + 0 = 3个字节。
LIMIT
的{{1}}可让优化程序为您提供感觉到的任何行。只需看几行就可以了,但对于Production来说这是危险的不可预测的。 (不,优化器可以自由忽略内部ORDER BY
。)提示:使用ORDER BY
,您不需要子查询(如Uueerdo所说):
HAVING
提示:InnoDB确实需要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 ;
。如果您没有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
,DATE
,DATETIME
,甚至是新的微秒精度数据类型都能正常工作。