Sqlite 改进 case-when 和 group by 性能
Sqlite improve case-when and group by performance
我正在使用 SQLite3 优化我的查询。
有一些 "CASE WHEN"、"GROUP BY"、"COUNT" 函数。
但是查询非常慢(大约 14 秒)
这是我的数据库文件信息。
- size: about 2GB
- rows : about 3 millions
- columns : 55 columns
我可以做什么来优化查询的性能?
有没有更好的查询结果?
请帮我TT谢谢
select
case
when score = 100 then 'A'
when score < 100 and score >= 40 then 'B'
else 'C'
end as range,
count(*) as count
from grade_info
where type < 9 and
(date >= '2019-07-09 00:00:00' and date <= '2019-07-09 23:59:59') and
is_new = 1
group by
case
when score = 100 then 'A'
when score < 100 and score >= 40 then 'B'
else 'C'
end;
Table grade_info
有多列索引:(type, date, is_new, score)
列的条件(类型、日期、is_new)始终用于此查询。这是解释查询计划的结果。
selectid | order | from | detail
--------------------------------
0 0 0 SEARCH TABLE grade_info USING INDEX idx_03 (type<?) (~2777 rows)
0 0 0 USE TEMP B-TREE FOR GROUP BY
我想要这样的结果。
A | 5124
B | 124
C | 12354
正如 Shawn 所建议的,尝试更改索引以将 date
列作为第一列:
CREATE INDEX [idx_cover] ON [grade_info] ([date], [is_new], [type], [score]);
sqlite 允许在 WHERE 和 GROUP BY 子句中引用别名表达式,因此您可以简单地说 GROUP BY range
而不是重复 CASE 语句。这可能不会改变效率,但会使查询更短且更易读。
如果您按照 MikeT 的建议运行 ANALYZE
,执行计划应该更改为 "COVERING INDEX..."。如果我理解正确,这表明整个查询可以通过遍历单个 multi-column 索引来执行,而无需返回到 table 数据。
尝试日期BETWEEN '2019-07-09 00:00:00' AND '2019-07-09 23:59:59'
.
最后,CASE...WHEN 是short-circuited,所以一定要先把可能性大的情况放在前面,这样可以避免不必要的计算。同时消除多余的条件检查。如果您已经在上一个条件中检查了某个范围,则无需在下一个条件中 re-evaluate 该范围。 (如果你已经排除了 score = 100,那么就没有必要检查 score < 100 因为它当然会小于 100 ...假设所有的分数都确保在 0 到 100 的范围内)例如,如果分数均匀分布,那么以下可能会更快,可能会消除 +17000 次条件检查。
SELECT
CASE
WHEN score < 40 then 'C'
WHEN score < 100 then 'B' -- already tested to be >= 40
ELSE 'A' -- already tested to be >= 100
END AS range,
count(*) AS count
FROM grade_info
WHERE type < 9 AND
(date BETWEEN '2019-07-09 00:00:00' AND '2019-07-09 23:59:59') AND
is_new = 1
GROUP BY
range;
我正在使用 SQLite3 优化我的查询。 有一些 "CASE WHEN"、"GROUP BY"、"COUNT" 函数。
但是查询非常慢(大约 14 秒)
这是我的数据库文件信息。
- size: about 2GB
- rows : about 3 millions
- columns : 55 columns
我可以做什么来优化查询的性能?
有没有更好的查询结果?
请帮我TT谢谢
select
case
when score = 100 then 'A'
when score < 100 and score >= 40 then 'B'
else 'C'
end as range,
count(*) as count
from grade_info
where type < 9 and
(date >= '2019-07-09 00:00:00' and date <= '2019-07-09 23:59:59') and
is_new = 1
group by
case
when score = 100 then 'A'
when score < 100 and score >= 40 then 'B'
else 'C'
end;
Table grade_info
有多列索引:(type, date, is_new, score)
列的条件(类型、日期、is_new)始终用于此查询。这是解释查询计划的结果。
selectid | order | from | detail
--------------------------------
0 0 0 SEARCH TABLE grade_info USING INDEX idx_03 (type<?) (~2777 rows)
0 0 0 USE TEMP B-TREE FOR GROUP BY
我想要这样的结果。
A | 5124
B | 124
C | 12354
正如 Shawn 所建议的,尝试更改索引以将
date
列作为第一列:CREATE INDEX [idx_cover] ON [grade_info] ([date], [is_new], [type], [score]);
sqlite 允许在 WHERE 和 GROUP BY 子句中引用别名表达式,因此您可以简单地说
GROUP BY range
而不是重复 CASE 语句。这可能不会改变效率,但会使查询更短且更易读。如果您按照 MikeT 的建议运行
ANALYZE
,执行计划应该更改为 "COVERING INDEX..."。如果我理解正确,这表明整个查询可以通过遍历单个 multi-column 索引来执行,而无需返回到 table 数据。尝试日期
BETWEEN '2019-07-09 00:00:00' AND '2019-07-09 23:59:59'
.最后,CASE...WHEN 是short-circuited,所以一定要先把可能性大的情况放在前面,这样可以避免不必要的计算。同时消除多余的条件检查。如果您已经在上一个条件中检查了某个范围,则无需在下一个条件中 re-evaluate 该范围。 (如果你已经排除了 score = 100,那么就没有必要检查 score < 100 因为它当然会小于 100 ...假设所有的分数都确保在 0 到 100 的范围内)例如,如果分数均匀分布,那么以下可能会更快,可能会消除 +17000 次条件检查。
SELECT
CASE
WHEN score < 40 then 'C'
WHEN score < 100 then 'B' -- already tested to be >= 40
ELSE 'A' -- already tested to be >= 100
END AS range,
count(*) AS count
FROM grade_info
WHERE type < 9 AND
(date BETWEEN '2019-07-09 00:00:00' AND '2019-07-09 23:59:59') AND
is_new = 1
GROUP BY
range;