以下是我真正问题的简化版本:表中有两列和一个索引。也就是说,
CREATE TABLE T (a integer primary key, b integer not null);
CREATE INDEX Ti on T (b, a);
列a是主键,因此唯一但列b可以有许多重复项。有问题的查询就像
SELECT * from T where b=5 and a>3 order by b, a limit 1;
我的期望是,只有一个二进制搜索应该足以找到满足条件的最小(b,a)对,如果它充分利用了索引。此外,the document on SQLite query planner明确指出
如果索引的初始列(列a,b等)出现在WHERE子句中,则可以使用索引。索引的初始列必须与=或IN或IS NULL运算符一起使用。 使用的最右列可以使用不等式。 对于使用的索引的最右列,最多可以有两个不等式必须将允许两个极端之间的列值。
但是explain
和explain query plan
的结果非常令人失望。 (sqlite3 3.8.8.3)
sqlite> explain query plan select * from T where b=5 and a>3 order by b,a limit 1;
0 0 0 SEARCH TABLE T USING COVERING INDEX Ti (b=?)
sqlite> explain select * from T where b=5 and a>3 order by b,a limit 1;
5 SeekGE 2 14 2 1 00
6 IdxGT 2 14 2 1 00
7 IdxRowid 2 3 0 00
8 Le 4 13 3 54
9 Copy 3 5 0 00
10 Column 2 0 6 00
11 ResultRow 5 2 0 00
12 IfZero 1 14 -1 00
13 Next 2 6 0 00
当然,它仅使用索引来定位b = 5的第一行,然后进行线性扫描以找到a> 3的行。当只有少量行具有重复的b值时,它可能没问题,但否则可能是一个问题。而不是p4 = 1的SeekGE,具有p4 = 2的SeekGT和具有p4 = 1的IdxGT可以更有效,因为它可以仅通过一次二分搜索直接定位右行。
所以问题是,当然,如果你说这就是它现在的方式,那么我可以做的并不多,但为了以防万一我还有什么缺少可以使其更好地工作? ANALYZE
不是一个选项,因为这是一种通常更好的方法来处理范围查询,而不仅仅是在特定的数据集上。