Sqlite 改进 case-when 和 group by 性能

Sqlite improve case-when and group by performance

我正在使用 SQLite3 优化我的查询。 有一些 "CASE WHEN"、"GROUP BY"、"COUNT" 函数。

但是查询非常慢(大约 14 秒)

这是我的数据库文件信息。

  1. size: about 2GB
  2. rows : about 3 millions
  3. 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;