我有一个如下查询:
Select
sum(r.impressions) as impressions from keyword_report r
where r.org_id = 1
and r.report_date between '2019-09-01' and '2019-09-10'
group by r.country, r.keyword_id;
我在 keywords_report 上有 2 个索引;
index1: (org_id, report_date)
index2: (country, keyword_id)
解释format=json结果:
{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "138210.60"
},
"grouping_operation": {
"using_temporary_table": true,
"using_filesort": false,
"table": {
"table_name": "r",
"access_type": "ref",
"possible_keys": [
"index1",
"index2"
],
"key": "index1",
"used_key_parts": [
"org_id",
"report_date"
],
"key_length": "11",
"ref": [
"const",
"const"
],
"rows_examined_per_scan": 125646,
"rows_produced_per_join": 125646,
"filtered": "100.00",
"index_condition": "(`r`.`report_date` between '2019-09-01' and '2019-09-10')",
"cost_info": {
"read_cost": "125646.00",
"eval_cost": "12564.60",
"prefix_cost": "138210.60",
"data_read_per_join": "162M"
},
"used_columns": [
"org_id",
"keyword_id",
"impressions",
"report_date",
"country"
]
}
}
}
}
表中大约有;
- 1000 个不同的 org_id,
- 500 个不同的 report_date,
- 30 个不同的国家/地区,
- 1000 万keyword_id。
我无法理解这里的两件事。
为什么使用临时?
为什么多个索引不起作用?
那么我该如何改进呢?
最佳答案
以下 JSON 输出似乎表明您的 index1
索引正在被使用:
"used_key_parts": [
"org_id",
"report_date"
]
可以在 WHERE
子句中使用此索引来过滤掉不匹配的记录。此后,MySQL 仍然必须执行 GROUP BY
聚合,其中包括对 impressions
列的求和。请注意,索引对于聚合并没有太大帮助,因为根据定义,数据库必须接触每个组中的每个记录才能计算总和。虽然大多数时候数据库甚至不会选择在同一个表上使用两个不同的索引(但这是可能的),但在这种情况下,第二个 index2
索引在这里没有多大帮助,因为您的聚合的性质。
为了给出一个可以使用单个索引来涵盖查询的所有步骤的示例,请考虑以下内容:
SELECT
r.country,
MAX(r.impressions) AS max_impressions
FROM keyword_report r
WHERE
r.org_id = 1 AND
r.report_date BETWEEN '2019-09-10' AND '2019-09-10'
GROUP BY
r.country;
现在,如果您定义了以下索引:
(org_id, report_date, country, impressions)
那么MySQL可能会选择使用它。它之所以有效,是因为在过滤掉 WHERE
子句中的记录后,可以轻松找到每个国家/地区的 impressions
最大值。
关于Mysql多重索引对单个查询不起作用(Group By + RangeWhere条件),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58049840/