SQLite queryslow 使用索引时

SQLite queryslow when using index

我在一个文本列上建立了 table 索引,我希望我对 return 结果的所有查询都按名称排序,而不会影响性能。 Table 如果重要的话,大约有 100 万行。

Table -

CREATE TABLE table (Name text)

索引-

CREATE INDEX "NameIndex" ON "Files" (
    "Name" COLLATE nocase   ASC
);

查询 1 -

select * from table where Name like "%a%"

查询计划,如预期的那样完整扫描 -

SCAN TABLE table

时间-

Result: 179202 rows returned in 53ms

查询 2,现在使用 order by 从索引读取 -

select * from table where Name like "%a%" order by Name collate nocase

查询计划,使用索引扫描-

SCAN TABLE table USING INDEX NameIndex

时间-

Result: 179202 rows returned in 672ms

使用 DB Browser for SQLite 获取上述信息,默认 Pragmas。

我假设扫描索引与扫描 table 一样高效,是不是这样还是我做错了什么?

我注意到的另一件有趣的事情,可能与此有关 -

查询 3 -

select * from table where Name like "a%"
Result: 23026 rows returned in 9ms

查询 4 ​​-

select * from table where name like "a%" order by name collate nocase
Result: 23026 rows returned in 101ms

并且两者都有相同的查询计划 -

SEARCH TABLE table USING INDEX NameIndex (Name>? AND Name<?)

这是预期的吗?如果计划相同,我会假设性能相同。

谢谢!

编辑 - 查询变慢的原因是因为我使用了 select * 而不是 select name,导致 SQLite 在 table 和索引之间移动。

解决方案是使用 clustered index,感谢@Tomalak 帮我找到它 - create table mytable (a text, b text, primary key (a,b)) without rowid table 将默认使用 a + b 组合进行排序,这意味着全扫描查询会快得多(现在为 90 毫秒)。

% 开头的 LIKE 模式永远不能使用索引。它将始终导致完整 table 扫描(或索引扫描,如果查询可以被索引本身覆盖)。

想想也是顺理成章的。索引不是魔法。它们是经过排序的值列表,就像书中的关键字索引一样,这意味着如果您知道给定单词的开头,它们只会快速查找单词。如果要搜索单词的中间部分,则还必须查看书中的每个索引条目。


随后discussion in the comments的结论:

要获得始终按 non-unique 列排序且不影响性能的 table,最好的做法是在不使用 ROWID 的情况下创建它,并将其转换为 clustering index 在有问题的列加上使组合唯一的第二列:

CREATE TABLE MyTable (
    Name   TEXT COLLATE NOCASE,
    Id     INTEGER,
    Other  TEXT,
    Stuff  INTEGER,
    PRIMARY KEY(Name, Id)  -- this will sort the whole table by Name
) WITHOUT ROWID;

这将导致 INSERT/UPDATE/DELETE 操作的性能下降,但作为交换,排序将是免费的,因为 table 已经被排序。