为什么 != 运算符比 = 运算符快?

Pie*_*rre 5 sql sqlite

我有一个 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)

为什么等于运算符这么慢?我的查询有什么问题吗?


繁殖步骤:

  1. 创建以下表
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)
  1. 使用以下 Perl 脚本填充表
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)

bwt*_*bwt 1

第一步的区别是可以理解的:=允许 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)