2015-08-27 17 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,甚至是新的微秒精度數據類型。