为什么SQLite在添加JOIN时拒绝使用可用索引?

问题描述 投票:5回答:3

这与Why is SQLite refusing to use available indexes?有关

创建数据库的查询是:

CREATE TABLE foo(id TEXT);
CREATE INDEX `foo.index` ON foo(id);
CREATE TABLE bar(id TEXT);
CREATE INDEX `bar.index` ON bar(id);
CREATE VIEW baz AS SELECT id FROM foo UNION ALL SELECT id FROM bar;
CREATE TABLE bam(id TEXT, value TEXT);

INSERT INTO foo VALUES('123');
INSERT INTO foo VALUES('1123');
INSERT INTO foo VALUES('2123');
INSERT INTO foo VALUES('3123');

INSERT INTO bar VALUES('44123');
INSERT INTO bar VALUES('441123');
INSERT INTO bar VALUES('442123');
INSERT INTO bar VALUES('443123');

EXPLAIN QUERY PLAN SELECT * FROM baz LEFT JOIN bam ON baz.id=bam.id WHERE baz.id IN ('123', '234');的结果是:

SCAN TABLE foo (~1000000 rows)
SCAN TABLE bar (~1000000 rows)
COMPOUND SUBQUERIES 2 AND 3 (UNION ALL)
SCAN SUBQUERY 1 (~2000000 rows)
EXECUTE LIST SUBQUERY 4
SEARCH TABLE bam USING AUTOMATIC COVERING INDEX (id=?) (~7 rows)

编辑:有趣的是,如果我做EXPLAIN QUERY PLAN SELECT * FROM (SELECT * FROM baz WHERE baz.id IN ('123', '234')) AS t LEFT JOIN bam ON t.id=bam.id ;它仍然不使用索引,但如果我做EXPLAIN QUERY PLAN SELECT * FROM baz WHERE baz.id IN ('123', '234');它确实。到底是怎么回事?

为什么不使用foo和bar上的索引?它确实使用没有JOIN部分的索引,如链接问题中所示。

SQL小提琴:http://sqlfiddle.com/#!7/32af2/14(使用WebSQL)

sql sqlite query-optimization
3个回答
4
投票

不使用索引,因为它们不是必需的;他们不会加快查询速度。

在SQLite中,连接被实现为嵌套循环连接,也就是说,数据库遍历一个表的所有记录,并且对于每个记录,查找另一个表中的匹配记录。只有第二个表中的查找才需要索引;只是浏览第一个表的所有记录不需要索引。

使用内部联接,查询优化器可以选择循环中的外部表或内部表(如果只有一个表具有索引,则应该是内部表)。但是,使用左外连接时,没有选择,左表必须是外表。

要优化左外连接,(仅)右侧的表需要索引。


4
投票

查询规划器确定在这种情况下使用索引不再有效。但是,仍然可以通过以下方式修改视图来强制使用索引:

CREATE VIEW baz AS SELECT id FROM foo UNION ALL SELECT id FROM bar ORDER BY id;

ORDER BY语句将在访问索引字段时强制使用索引。

新查询计划的结果:

EXPLAIN QUERY PLAN SELECT * FROM baz LEFT JOIN bam ON baz.id=bam.id WHERE baz.id IN ('123', '234');

SCAN TABLE foo USING COVERING INDEX foo.index (~4 rows)
SCAN TABLE bar USING COVERING INDEX bar.index (~4 rows)
COMPOUND SUBQUERIES 2 AND 3 (UNION ALL)
SCAN SUBQUERY 1 (~2 rows)
EXECUTE LIST SUBQUERY 4
SEARCH TABLE bam USING INDEX bam.index (id=?) (~1 rows)

0
投票

Union All非常难以优化,因为dbms将生成结果的临时副本。您必须专门找到有关如何实现union all的文档,但您可以在foobar列中进行设计。

CREATE TABLE Danny117(id TEXT, mytype TEXT);

INSERT INTO Danny117 VALUES('123', 'foo');
INSERT INTO Danny117 VALUES('1123', 'foo');
INSERT INTO Danny117 VALUES('2123', 'foo');
INSERT INTO Danny117 VALUES('3123', 'foo');

INSERT INTO Danny117 VALUES('44123','bar');
INSERT INTO Danny117 VALUES('441123','bar');
INSERT INTO Danny117 VALUES('442123','bar');
INSERT INTO Danny117 VALUES('443123','bar');

的结果

EXPLAIN QUERY PLAN SELECT Danny117.id, bam.* FROM Danny117 LEFT JOIN bam ON danny117.id=bam.id WHERE danny117.id IN ('123', '234');

祝好运

© www.soinside.com 2019 - 2024. All rights reserved.