1

考虑以下示例:

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);

此查询创建没有 rowid 列的表并插入值(x,x),其中 1000 < x < 2000。为了帮助查询规划器,让我们运行 ANALYZE。

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<?) 但是,使用索引(对于第一个查询)是没有意义的,因为无论如何我们都必须遍历整个表,所以普通的 SCAN TABLE 似乎更有效。正是以这种方式使用 rowid 的表工作:

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 表的查询吗?

4

1 回答 1

2

两种情况下的输出都是: 0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1b (b>? AND b<?)

但是,使用索引(对于第一个查询)是没有意义的,因为无论如何我们都必须遍历整个表,所以普通的 SCAN TABLE 似乎更有效。

您错过了COVERING INDEX部分:这意味着它只使用索引 - 根本不访问表。

如果需要所有行,则常规索引访问(没有“COVERING”)可能比全表扫描慢,但仅索引扫描并非如此。

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

编辑

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

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

于 2017-10-09T12:28:44.933 回答