文本列上的索引可以加速基于前缀的 LIKE 查询吗?
Can an index on a text column speed up prefix based LIKE queries?
在 SQLite 中,文本列上的索引能否加速基于前缀的 LIKE
列查询?
例如,如果我有一个名为 path
的 TEXT
列并且我 运行 一个类似于 WHERE path LIKE '/path/to/some/dir/%'
的查询,这个查询是否能够从一个path
列的索引?
LIKE
不会从索引中获益(使用默认选项),但您可以使用 GLOB
或 BETWEEN
重写查询。
解决方案 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
因为它已经隐含了。
在 SQLite 中,文本列上的索引能否加速基于前缀的 LIKE
列查询?
例如,如果我有一个名为 path
的 TEXT
列并且我 运行 一个类似于 WHERE path LIKE '/path/to/some/dir/%'
的查询,这个查询是否能够从一个path
列的索引?
LIKE
不会从索引中获益(使用默认选项),但您可以使用 GLOB
或 BETWEEN
重写查询。
解决方案 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
因为它已经隐含了。