【问题标题】:sqlite choose wrong query plansqlite 选择错误的查询计划
【发布时间】:2018-03-20 15:38:49
【问题描述】:

考虑以下示例:

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

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&gt;? AND b&lt;?) 但是,使用索引(对于第一个查询)是没有意义的,因为无论如何我们都必须遍历整个表,所以普通的 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 表的查询?

【问题讨论】:

    标签: sql sqlite query-optimization query-planner


    【解决方案1】:

    两种情况的输出都是: 0|0|0|搜索表 t1 使用覆盖索引 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

    【讨论】:

    • 那么为了优化没有rowid的表的查询而运行analyze是没有意义的?
    • @NikitaRock 它可能会导致:数据库可能仍然选择在聚集索引之外使用另一个索引。
    • 但是无论如何,如果在表中添加第三列,那么 t1b 上的索引将不再是 COVERING(在 WITHOUT ROWID 情况下)。但是,查询规划器仍然更喜欢使用索引进行搜索,而不是全扫描表。
    • 而且,我真的没有得到这个:“无需访问表”。据我所知,在 SQLite 中,表被保存为 rowid 或主键的 b-tree 索引,并且没有堆表。
    • 不管有多少列,一个聚集索引(没有 ROWID)都有所有的列。正如您所说:如果表没有 ROWID,则没有其他人工结构可供访问(在 SQLite 的情况下,“堆”实际上也是一个 b 树)。
    猜你喜欢
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多