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 已经被排序。
我在一个文本列上建立了 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 已经被排序。