文本列上的索引可以加速基于前缀的 LIKE 查询吗?

Can an index on a text column speed up prefix based LIKE queries?

在 SQLite 中,文本列上的索引能否加速基于前缀的 LIKE 列查询?

例如,如果我有一个名为 pathTEXT 列并且我 运行 一个类似于 WHERE path LIKE '/path/to/some/dir/%' 的查询,这个查询是否能够从一个path 列的索引?

LIKE 不会从索引中获益(使用默认选项),但您可以使用 GLOBBETWEEN 重写查询。

解决方案 1

使用常规索引:

喜欢 => 未优化

sqlite> explain query plan select * from pathdta where path like '/path/to/some/dir/a%' ;
0|0|0|SCAN TABLE pathdta

GLOB => 优化

sqlite> explain query plan select * from pathdta where path GLOB '/path/to/some/dir/a*' ;
0|0|0|SEARCH TABLE pathdta USING COVERING INDEX ix_pathdta_dta (path>? AND path<?)

更好 => 优化

sqlite> explain query plan  select * from pathdta where path >= '/path/to/some/dir/a' ;
0|0|0|SEARCH TABLE pathdta USING COVERING INDEX ix_pathdta_dta (path>?)

平等 => 优化

sqlite> explain query plan  select * from pathdta where path = '/path/to/some/dir/a' ;
0|0|0|SEARCH TABLE pathdta USING COVERING INDEX ix_pathdta_dta (path=?)

之间 => 优化

sqlite> explain query plan  select * from pathdta
   ...>    where path between '/path/to/some/dir/a' and '/path/to/some/dir/b' ;
0|0|0|SEARCH TABLE pathdta USING COVERING INDEX ix_pathdta_dta (path>? AND path<?)

解决方案 2

使用 collate nocase 索引。

喜欢 => 优化

sqlite> explain query plan select * from pathdta where path like '/path/to/some/dir/a%' ;
0|0|0|SEARCH TABLE pathdta USING COVERING INDEX ix_pathdta_dta (path>? AND path<?)

glob => 未优化

sqlite> explain query plan select * from pathdta where path GLOB '/path/to/some/dir/a*' ;
0|0|0|SCAN TABLE pathdta

更好 => 未优化

sqlite> explain query plan  select * from pathdta where path >= '/path/to/some/dir/a' ;
0|0|0|SCAN TABLE pathdta

平等 => 未优化

sqlite> explain query plan  select * from pathdta where path = '/path/to/some/dir/a' ;
0|0|0|SCAN TABLE pathdta

之间 => 未优化

sqlite> explain query plan  select * from pathdta
   ...>    where path between '/path/to/some/dir/a' and '/path/to/some/dir/b' ;
0|0|0|SCAN TABLE pathdta

是的,会的,索引正确。如果使用默认的不区分大小写的LIKE模式,索引也需要不区分大小写。还有其他限制,请参阅 LIKE optimization documentation 了解完整详细信息(它很长且难以总结)。

示例:

sqlite> CREATE TABLE paths(id INTEGER PRIMARY KEY, path TEXT);
sqlite> CREATE INDEX paths_idx_path ON paths(path); -- case sensitive index
sqlite> EXPLAIN QUERY PLAN SELECT * FROM paths WHERE path LIKE 'foo%';
QUERY PLAN
`--SCAN TABLE paths
sqlite> DROP INDEX paths_idx_path;
sqlite> CREATE INDEX paths_idx_path ON paths(path COLLATE NOCASE); -- case insensitive index
sqlite> EXPLAIN QUERY PLAN SELECT * FROM paths WHERE path LIKE 'foo%';
QUERY PLAN
`--SEARCH TABLE paths USING COVERING INDEX paths_idx_path (path>? AND path<?)

如您所见,使用不区分大小写的索引,查询被重写以搜索特定范围内的行,而不是扫描 table.[=16= 中的所有行]

您还可以在 table 定义中指定列的所有比较不区分大小写:

CREATE TABLE paths(id INTEGER PRIMARY KEY, path TEXT COLLATE NOCASE);

然后索引不需要 COLLATE 因为它已经隐含了。