我有一个 SQLite 数据库,我执行以下查询:
SELECT
category,
name,
COUNT(side.id) as nb
FROM main
LEFT JOIN side
ON main.id = side.id_main
WHERE category != 3
GROUP BY category, name;
Run Code Online (Sandbox Code Playgroud)
此查询大约需要 0.3 秒并返回 8000 行。这是关联的查询计划:
id parent notused detail
7 0 0 SCAN TABLE main USING COVERING INDEX idx_main
19 0 0 SEARCH TABLE side USING AUTOMATIC COVERING INDEX (id_main=?)
Run Code Online (Sandbox Code Playgroud)
现在,我只想选择一个类别。所以,我改写了我的WHERE条款是这样的:
id parent notused detail
7 0 0 SCAN TABLE main USING COVERING INDEX idx_main
19 0 0 SEARCH TABLE side USING AUTOMATIC COVERING INDEX (id_main=?)
Run Code Online (Sandbox Code Playgroud)
这个查询现在需要大约 43 秒并返回 5000 行!长了100倍!这是关联的查询计划:
id parent notused detail
7 0 0 SEARCH TABLE main USING COVERING INDEX idx_main (category=?)
11 0 0 SCAN TABLE side
Run Code Online (Sandbox Code Playgroud)
为什么等于运算符这么慢?我的查询有什么问题吗?
繁殖步骤:
SELECT
category,
name,
COUNT(side.id) as nb
FROM main
LEFT JOIN side
ON main.id = side.id_main
WHERE category = 1
GROUP BY category, name;
Run Code Online (Sandbox Code Playgroud)
id parent notused detail
7 0 0 SEARCH TABLE main USING COVERING INDEX idx_main (category=?)
11 0 0 SCAN TABLE side
Run Code Online (Sandbox Code Playgroud)
第一步的区别是可以理解的:=允许 SQLite 查找给定值的行 ( SEARCH),而!=要求它遍历所有值 ( SCAN)
问题是:为什么它AUTOMATIC在第一种情况下创建临时 () 索引(这恰好非常有效),而不是在第二种情况下?
我认为这是因为,在没有附加信息的情况下,查询规划器假设一个=子句将产生比 a 更少的匹配项!=,并且构建临时索引的成本是不合理的。
解决方案是让 SQLite 使用ANALYZE收集信息。查询计划变为:
QUERY PLAN
|--SEARCH TABLE main USING COVERING INDEX idx_main (category=?)
`--SEARCH TABLE side USING AUTOMATIC COVERING INDEX (id_main=?)
Run Code Online (Sandbox Code Playgroud)
另一个解决方案是创建所需的索引,这样 SQLite 就不必构建它。
CREATE INDEX idx_side2 ON side (id_main);
Run Code Online (Sandbox Code Playgroud)
使用以下查询计划(即使没有分析):
QUERY PLAN
|--SEARCH TABLE main USING COVERING INDEX idx_main (category=?)
`--SEARCH TABLE side USING COVERING INDEX idx_side2 (id_main=?)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
103 次 |
| 最近记录: |