2017-10-06 31 views
-1

我有以下2個SQL查詢,它們是95%相同的,但性能顯着不同。2非常相似的SQL查詢有完全不同的性能

SQL查詢1(< 0,1s):

SELECT CONCAT(a.`report_year`, '-', a. `report_month`) as `yearmonth`, 
AVG(a.cost_leasing/b.rate*IF(`report_year`=2016,0.73235, 
     IF(`report_year`=2017,0.83430,1))) as average, 
'current' as `type` 
FROM `vehicles` as a, `exchange_rates` as b 
WHERE cid='3' AND 
STR_TO_DATE(CONCAT(`report_year`, '-', `report_month`, '-01'), 
     '%Y-%m-%d') >= '2016-01-01' AND 
LAST_DAY(STR_TO_DATE(CONCAT(`report_year`, '-', `report_month`, 
     '-01'), '%Y-%m-%d')) <= '2017-06-30' AND 
`country` IN ('XX','UK') AND 
a.currency = b.currency AND 
b.`year` = `report_year` AND 
fxid=2 
GROUP BY `yearmonth` 
ORDER BY `yearmonth`; 

解釋查詢1:

1 SIMPLE a ref new_selectors,... new_cost_leasing 4 const 10812 Using where; Using index; Using temporary; Using f... 
1 SIMPLE b ref PRIMARY,date,fxid fxid 19 const,c1682fleet.a.report_year,c1682fleet.a.curren... 196 Using where; Using index  

SQL查詢2(> 3秒):

SELECT CONCAT(c.`report_year`, '-', c.`report_month`) as `yearmonth`, 
AVG(c.cost_leasing/d.rate*IF(`report_year`=2016,0.73235, 
     IF(`report_year`=2017,0.83430,1))), 
'baseline' 
FROM `kpis` as c, `exchange_rates` as d 
WHERE cid='3' AND 
STR_TO_DATE(CONCAT(`report_year`, '-', `report_month`, '-01'), 
     '%Y-%m-%d') >= '2016-01-01' AND 
LAST_DAY(STR_TO_DATE(CONCAT(`report_year`, '-', `report_month`, 
      '-01'), '%Y-%m-%d')) <= '2017-06-30' AND 
`country` IN ('XX','UK') AND 
c.kid=1 AND 
c.currency = d.currency AND 
d.`year` = `report_year` AND 
fxid=2 
GROUP BY `yearmonth` 
ORDER BY `yearmonth`; 

解釋查詢2 :

1 SIMPLE c ref oem_group,... cost_leasing 8 const,const 30038 Using where; Using index; Using temporary; Using f... 
1 SIMPLE d ref PRIMARY,date,fxid fxid 19 const,c1682fleet.c.report_year,c1682fleet.c.curren... 196 Using where; Using index 

SHOW指數從車輛:

vehicles 0 PRIMARY 1 vid A 146068    BTREE   
vehicles 1 new_cost_leasing 1 cid A 12    BTREE   
vehicles 1 new_cost_leasing 2 cost_leasing A 4564    BTREE   
vehicles 1 new_cost_leasing 3 currency A 5216    BTREE   
vehicles 1 new_cost_leasing 4 report_month A 24344    BTREE   
vehicles 1 new_cost_leasing 5 report_year A 29213    BTREE   
vehicles 1 new_cost_leasing 6 country A 36517    BTREE   
vehicles 1 new_cost_leasing 7 supplier A 29213    BTREE   
vehicles 1 new_cost_leasing 8 jato_segment A 24344    BTREE   
vehicles 1 new_cost_leasing 9 business_unit A 36517    BTREE   
vehicles 1 new_cost_leasing 10 entity A 73034    BTREE   

SHOW指數從exchange_rates:

exchange_rates 0 PRIMARY 1 fxid A 2    BTREE   
exchange_rates 0 PRIMARY 2 currency A 160    BTREE   
exchange_rates 0 PRIMARY 3 date A 569250    BTREE   
exchange_rates 1 date 1 fxid A 2    BTREE   
exchange_rates 1 date 2 date A 28462    BTREE   
exchange_rates 1 date 3 currency A 569250    BTREE   
exchange_rates 1 date 4 rate A 569250    BTREE   
exchange_rates 1 fxid 1 fxid A 2    BTREE   
exchange_rates 1 fxid 2 year A 114    BTREE   
exchange_rates 1 fxid 3 currency A 2904    BTREE   
exchange_rates 1 fxid 4 rate A 569250    BTREE   

SHOW指數從關鍵績效指標:

kpis 0 PRIMARY 1 vid A 60308    BTREE     
kpis 1 cost_leasing 1 cid A 2    BTREE   
kpis 1 cost_leasing 2 kid A 2    BTREE   
kpis 1 cost_leasing 3 cost_leasing A 78    BTREE   
kpis 1 cost_leasing 4 currency A 78    BTREE   
kpis 1 cost_leasing 5 report_month A 1096    BTREE   
kpis 1 cost_leasing 6 report_year A 3350    BTREE   
kpis 1 cost_leasing 7 country A 1884    BTREE   
kpis 1 cost_leasing 8 supplier A 4020    BTREE   
kpis 1 cost_leasing 9 jato_segment A 3015    BTREE   
kpis 1 cost_leasing 10 business_unit A 4307    BTREE   
kpis 1 cost_leasing 11 entity A 6030    BTREE   
kpis 1 avg_cost 1 cid A 2    BTREE   
kpis 1 avg_cost 2 kid A 2    BTREE   
kpis 1 avg_cost 3 country A 48    BTREE   
kpis 1 avg_cost 4 report_year A 96    BTREE   
kpis 1 avg_cost 5 currency A 96    BTREE   
kpis 1 avg_cost 6 cost_leasing A 172    BTREE  

問題: 我的問題是,爲什麼會出現即使你是一個顯着的性能差異(因子30) gh在查詢2(kid)中只有一個附加標準,它甚至是索引的一部分。

任何想法如何優化查詢2?

+4

如果他們使用完全不同的表格,我不會說他們95%是相同的。有可能不同的結構,索引,記錄數量......需要更多信息。 –

+0

tabels與kpis另外包含字段'kid'這一事實相同。索引相同,除了kpi索引還包含'kid'作爲列的事實之外,它們是相等的。 您可以在解釋答案中看到受影響的行。 – faulix90

+0

你能發佈你的索引定義嗎? – eventHandler

回答

0

,我發現了問題所在:exchange_ratesyear不是唯一的和選擇的vehicles剛剛選擇的kpis的一半大小,但因爲沒有唯一的列year的大基數的創造exchange_rateskpis加盟一個臨時的超過200萬條目,這對於平均運作來說非常大。

解決方案:不要使用year我使用的唯一列date,改變了條件

`date` = MAKEDATE(`report_year`, 1) 
+0

如果您知道全局在ONE SELECT中使用的超過一百萬行沒有任何意義,那麼您的my.cnf/ini中的這一行sql_select_limit = 1M#來停止錯誤的數據量---在你任職期間會爲你工作 - 或者直到有人爲你的my.cfg移除它。 –

0

這些都不是優化搜索:

STR_TO_DATE(CONCAT(`report_year`, '-', `report_month`, '-01'), '%Y-%m-%d') >= '2016-01-01' 

LAST_DAY(STR_TO_DATE(CONCAT(`report_year`, '-', `report_month`, '-01'), '%Y-%m-%d')) <= '2017-06-30' 

您正在使用5個功能來連接每一行並轉換爲日期,但只有2個日期常量進行比較。如果可以將其逆轉並將2個日期常量轉換爲適合未改變數據的某些事件,那將節省大量的工作量。您不僅可以節省功能的計算工作量,還可以使用report_yearreport_month上的索引。

我還沒有時間來測試這麼多,我猜測invloved的列是整數,但我認爲日期範圍處理的一組更多的謂詞將有助於這兩個查詢。例如

SQL Fiddle

的MySQL 5。6架構設置

CREATE TABLE Table1 
    (`Report_Year` int, `Report_Month` int) 
; 

INSERT INTO Table1 
    (`Report_Year`, `Report_Month`) 
VALUES 
    (2015, 1), (2015, 2), (2015, 3), 
    (2015, 4), (2015, 5), (2015, 6), 
    (2015, 7), (2015, 8), (2015, 9), 
    (2015, 10), (2015, 11), (2015, 12), 
    (2016, 1), (2016, 2), (2016, 3), 
    (2016, 4), (2016, 5), (2016, 6), 
    (2016, 7), (2016, 8), (2016, 9), 
    (2016, 10), (2016, 11), (2016, 12), 
    (2017, 1), (2017, 2), (2017, 3), 
    (2017, 4), (2017, 5), (2017, 6), 
    (2017, 7), (2017, 8), (2017, 9), 
    (2017, 10), (2017, 11), (2017, 12) 
; 

**查詢**:

set @start := '2016-04-04'; 
set @end := '2017-01-30'; 

select *, @start, @end 
from table1 
where (
     ((year(@start) < year(@end)) AND report_year = year(@start) and report_month >= month(@start)) 
     OR 
     ((year(@start) < year(@end)) AND report_year > year(@start) and report_year < year(@end)) 
     OR 
     ((year(@start) <= year(@end)) AND report_year = year(@end) and report_month <= month(@end)) 
    ) 

[結果]

| Report_Year | Report_Month |  @start |  @end | 
|-------------|--------------|------------|------------| 
|  2016 |   4 | 2016-04-04 | 2017-01-30 | 
|  2016 |   5 | 2016-04-04 | 2017-01-30 | 
|  2016 |   6 | 2016-04-04 | 2017-01-30 | 
|  2016 |   7 | 2016-04-04 | 2017-01-30 | 
|  2016 |   8 | 2016-04-04 | 2017-01-30 | 
|  2016 |   9 | 2016-04-04 | 2017-01-30 | 
|  2016 |   10 | 2016-04-04 | 2017-01-30 | 
|  2016 |   11 | 2016-04-04 | 2017-01-30 | 
|  2016 |   12 | 2016-04-04 | 2017-01-30 | 
|  2017 |   1 | 2016-04-04 | 2017-01-30 | 

[結果]

set @start := '2016-01-01'; 
set @end := '2016-06-30'; 

| Report_Year | Report_Month |  @start |  @end | 
|-------------|--------------|------------|------------| 
|  2016 |   1 | 2016-01-01 | 2016-06-30 | 
|  2016 |   2 | 2016-01-01 | 2016-06-30 | 
|  2016 |   3 | 2016-01-01 | 2016-06-30 | 
|  2016 |   4 | 2016-01-01 | 2016-06-30 | 
|  2016 |   5 | 2016-01-01 | 2016-06-30 | 
|  2016 |   6 | 2016-01-01 | 2016-06-30 | 

set @start := '2016-01-01'; 
set @end := '2017-06-30'; 

[結果]

| Report_Year | Report_Month |  @start |  @end | 
|-------------|--------------|------------|------------| 
|  2016 |   1 | 2016-01-01 | 2017-06-30 | 
|  2016 |   2 | 2016-01-01 | 2017-06-30 | 
|  2016 |   3 | 2016-01-01 | 2017-06-30 | 
|  2016 |   4 | 2016-01-01 | 2017-06-30 | 
|  2016 |   5 | 2016-01-01 | 2017-06-30 | 
|  2016 |   6 | 2016-01-01 | 2017-06-30 | 
|  2016 |   7 | 2016-01-01 | 2017-06-30 | 
|  2016 |   8 | 2016-01-01 | 2017-06-30 | 
|  2016 |   9 | 2016-01-01 | 2017-06-30 | 
|  2016 |   10 | 2016-01-01 | 2017-06-30 | 
|  2016 |   11 | 2016-01-01 | 2017-06-30 | 
|  2016 |   12 | 2016-01-01 | 2017-06-30 | 
|  2017 |   1 | 2016-01-01 | 2017-06-30 | 
|  2017 |   2 | 2016-01-01 | 2017-06-30 | 
|  2017 |   3 | 2016-01-01 | 2017-06-30 | 
|  2017 |   4 | 2016-01-01 | 2017-06-30 | 
|  2017 |   5 | 2016-01-01 | 2017-06-30 | 
|  2017 |   6 | 2016-01-01 | 2017-06-30 | 
+0

謝謝你們的例子,努力工作的'sargable謂詞'。 –

+0

缺少分號,請在sqlfiddle.com的查詢中再次,謝謝。 –

+0

sqlfiddle退出的冒號,但在選擇標記佈局時被丟棄...可能的sqlfiddle在該佈局中的錯誤。 –

0

可優化搜索是優點。更好地處理dates是一個重點。這裏有一些更多的觀點。

11列指數幾乎保證是浪費。即使是6列指數也不太可能被充分利用。只有索引最左邊的列將被使用。通常它會到達下一列無用的點,所以它會停止。

通常將日期分成年,月和日不是一個好主意。由於您似乎只需要年份和月份,建議值爲CHAR(7) CHARSET ascii,值爲'2017-06'。或者你真的有報告在一個月中停止?

請確認每個列名與所涉及的表一致。我們知道哪個表,例如,fxid是這是非常重要的

請使用JOIN .. ON語法:(我寧願AS vAS er作爲助記符)

FROM vehicles AS a 
JOIN exchange_rates AS b ON a.currency = b.currency 

期望的索引(與當前的年/月列):

b,d: INDEX(fxid, currency, year) 
a: INDEX(cid, currency, report_year) 
c: INDEX(kid, cid, currency, report_year) 

更多關於創建索引:http://mysql.rjweb.org/doc.php/index_cookbook_mysql

相關問題