2017-10-06 4 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(> 3S):

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のINDEX:exchange_rates FROM

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のINDEX:KPIのFROM

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のINDEX:

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(子)には索引の一部でもある追加の基準が1つしかありません。

誰でも私はどのようにクエリ2を最適化できますか?

+4

完全に異なるテーブルを使用している場合、同じ95%であるとは言いません。潜在的に異なる構造、索引、レコード数...必要な情報が増えます。 –

+0

のタブは、kpisにさらにフィールド「kid」が含まれていることと同じです。インデックスも同じですが、kpiインデックスに 'kid 'が列として追加されていることに匹敵します。 説明の回答に表示される影響を受ける行。 – faulix90

+0

インデックス定義を投稿できますか? – eventHandler

答えて

0

私は問題を発見:exchange_ratesyearは一意ではありませんでしたし、vehiclesの選択はkpisの選択のちょうど半分の大きさを持っていましたが、理由は一意ではない列yearの大きなカーディナリティのexchange_ratesと作成kpisの参加一時的な2百万件以上のエントリがあり、これは平均的な操作では非常に大きなものです。

ソリューション:私は、一意の列dateを使用し、これらは、検索引数可能ではありません

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

ONE SELECT内で使用されている1ミリオン以上の行をグローバルに知っていれば、my.cnf/iniのこの行は意味をなさないsql_select_limit = 1M#誤った量のデータを停止する---あなたのテニュアの間、またはあなたのmy.cfgのために削除するまであなたのために働くでしょう。 –

0

に条件を変更する代わりにyearを使用する:あなたが連結し5つの機能を使用しているすべての行については

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' 

日付に変換しますが、2つの日付定数が比較されます。これを逆にして、2つの日付定数を変換されていないデータに適したものに変換することができれば、多大な労力を節約できます。関数の計算にかかる労力を節約できるだけでなく、report_yearreport_monthのインデックスを使用できる可能性があります。

私はこれほど多くの時間をテストしていませんでした。私は列が整数であると推測していますが、日付範囲の処理のためのより賢明な述語セットが両方のクエリを助けると思います。例えば

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構文を使用してください:

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

(私はニーモニックとしてAS vAS erを好むだろう。)

望ましいインデックス(現在の年/月のカラム):

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

関連する問題