Mysql 2个非常相似的SQL查询具有完全不同的性能
我有以下两个SQL查询,它们95%相同,但性能明显不同 SQL查询1(3s): 显示来自车辆的索引:Mysql 2个非常相似的SQL查询具有完全不同的性能,mysql,sql,select,indexing,query-performance,Mysql,Sql,Select,Indexing,Query Performance,我有以下两个SQL查询,它们95%相同,但性能明显不同 SQL查询1(3s): 显示来自车辆的索引: 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_
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
显示汇率指数:
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
显示KPI中的索引:
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
问题:
我的问题是,为什么在查询2中只有一个额外的条件(kid)(它甚至是索引的一部分)却存在如此显著的性能差异(因子30)
有人知道如何优化查询2吗?我发现了问题:汇率列
年份不是唯一的,车辆的选择只有KPI选择的一半,但由于非唯一列年份
的基数很大,汇率
和KPI
的联合创建了一个临时集合,包含200多万个条目,这对于平均操作来说是非常大的
解决方案:我没有使用year
,而是使用了唯一的列date
,并将条件更改为
`date` = MAKEDATE(`report_year`, 1)
这些都是不可取的:
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'
**查询**:
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))
)
[结果]:
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)
;
| 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 |
[结果]:
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)
;
| 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 |
萨尔盖博是一个优势。更好地处理日期是一个要点。这里还有一些要点
11列索引几乎肯定是一种浪费。即使是6列索引也不太可能得到充分利用。将仅使用索引中最左边的列。通常它会到达下一列不可用的点,因此它会停止
一般来说,将日期分为年、月和日不是一个好主意。由于您似乎只需要年份和月份,建议使用CHAR(7)CHARSET ascii
值,如“2017-06”。或者你真的有报告在一个月内停止?
请用所涉及的表限定每个列名。非常重要的是,我们知道哪个表,例如,fxid
请使用JOIN。。关于
语法:
FROM vehicles AS a
JOIN exchange_rates AS b ON a.currency = b.currency
(我更喜欢作为v
和作为er
作为助记符。)
所需索引(包含当前年份/生产任务列):
关于创建索引的更多信息:如果它们使用完全不同的表,我不会说它们95%相同。具有可能不同的结构、索引和记录数。。。需要更多信息。除了KPI额外包含字段kid
之外,选项卡是相同的。索引相同,除了kpi索引额外包含kid
as列之外,它们是相等的。您可以在解释答案中看到受影响的行。您可以发布您的索引定义吗?请阅读此内容,并特别注意有关查询性能的部分。请回答您的问题以提供更多信息。您的一个索引可能缺少一列,但我们无法确定是哪一列。我添加了相关的KPI索引。感谢您为“sargable谓词”提供的示例。再次感谢您,在sqlfiddle.com的查询中缺少分号。分号在sqlfiddle处退出,但在选择标记布局时被删除。。。布局中可能存在sqlfiddle错误。顺便说一句,如果更改数据模型是一个选项,我建议使用日期字段,而不是年份和月份列。如果您知道在一个SELECT中使用的全局行数超过一百万,则没有意义,您的my.cnf/ini中的这一行--sql_select_limit=1M#用于停止错误的数据量--将在您任职期间或在有人为您的my.cfg删除它之前对您有效。
b,d: INDEX(fxid, currency, year)
a: INDEX(cid, currency, report_year)
c: INDEX(kid, cid, currency, report_year)