sqlite 选择了错误的查询计划

sqlite choose wrong query plan

考虑以下示例:

DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
WITH RECURSIVE
    cnt(x) AS (VALUES(1000) UNION ALL SELECT x+1 FROM cnt WHERE x<2000)
INSERT INTO t1(a,b) SELECT x, x FROM cnt;
CREATE INDEX t1b ON t1(b);

此查询创建 table 没有 rowid 列并在其中插入值 (x, x) 1000 < x < 2000。为了帮助查询规划器让 运行 分析。

ANALYZE;

EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE b BETWEEN 500 AND 2500;

EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE b BETWEEN 2900 AND 3000;

两种情况下的输出都是:0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1b (b>? AND b<?) 但是,使用索引(对于第一个查询)是没有意义的,因为无论如何我们都必须遍历整个 table,所以普通的 SCAN TABLE 似乎更有效。正是这样 tables with rowid work:

DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a, b);
WITH RECURSIVE
   cnt(x) AS (VALUES(1000) UNION ALL SELECT x+1 FROM cnt WHERE x<2000)
INSERT INTO t1(a,b) SELECT x, x FROM cnt;
CREATE INDEX t1a ON t1(a);
ANALYZE;

EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 500 AND 2500;

EXPLAIN QUERY PLAN
SELECT * FROM t1 WHERE a BETWEEN 2900 AND 3000;

在这种情况下,输出将是:0|0|0|SCAN TABLE t1 0|0|0|SEARCH TABLE t1 USING INDEX t1a (a>? AND a<?)

那么,有人可以解释一下查询规划器如何针对 WITHOUT ROWID table 优化查询吗?

The output in both cases is: 0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1b (b>? AND b<?)

However, there is no sense to use index (for the first query) for the reason that anyway we have to iterate through whole table, so ordinary SCAN TABLE seems to be more efficient.

您错过了 COVERING INDEX 部分:这意味着它仅使用索引 — 根本没有访问 table。

你是对的,如果需要所有行,常规索引访问(没有“COVERING”)可能比完整 table 扫描慢,但仅索引扫描并非如此。

在此处阅读有关仅索引扫描的更多信息:http://use-the-index-luke.com/sql/clustering/index-only-scan-covering-index

编辑

WITHOUT ROWID 在 SQLite 中是所谓的 聚集索引 在其他数据库中:它们包含 所有 table 列.因此,无需访问 table,即使您 select 所有列(如 select *)。

在此处阅读有关聚簇索引的更多信息:http://use-the-index-luke.com/sql/clustering/index-organized-clustered-index