在不牺牲索引使用的情况下,在SQLite LIKE中转义通配符(%,_)?

axe*_*axe 10 .net c# sqlite indexing performance

我对SQLite查询有几个问题.实际上我开始认为SQLite不是为超过10行的表设计的,实际上,SQLite是一场噩梦.

以下查询

SELECT * FROM [Table] WHERE [Name] LIKE 'Text%'
Run Code Online (Sandbox Code Playgroud)

它工作正常.EXPLAIN显示使用索引并在about之后返回结果70ms.

现在我需要从.NET SQLite驱动程序运行此查询,所以我正在更改查询

SELECT * FROM [Table] WHERE [Name] LIKE @Pattern || '%'
Run Code Online (Sandbox Code Playgroud)

索引未使用.当我在任何SQLite工具中运行以下查询时,也不使用索引

SELECT * FROM [Table] WHERE [Name] LIKE 'Text' || '%'
Run Code Online (Sandbox Code Playgroud)

所以我猜SQLite没有实现任何类型的预处理逻辑.

好.让我们尝试解决它,我仍然绑定变量并执行以下操作

SELECT * FROM [Table] WHERE [Name] LIKE @Pattern
Run Code Online (Sandbox Code Playgroud)

但现在我将%通配符添加到我的模式字符串的末尾,就像这样

command.Parameters.Add(new SQLiteParameter("@Pattern", pattern + '%'));
Run Code Online (Sandbox Code Playgroud)

它工作得很慢.我不能说为什么,因为当我从SQLite工具运行此查询时它工作正常,但是当我从.NET代码绑定此变量时,它工作缓慢.

好.我还在努力解决这个问题.我正在摆脱模式参数绑定并动态构建此条件.

pattern = pattern.Replace("'", "''");
pattern = pattern.Replace("%", "\\%");
where = string.Format("LIKE '{0}%' ESCAPE '\\'", pattern);
Run Code Online (Sandbox Code Playgroud)

索引不再使用.它没有被使用,因为ESCAPE.当我跑步时,我看到了

EXPLAIN QUERY PLAN SELECT * FROM [Table] WHERE [Name] LIKE 'Text%' ESCAPE '\'
Run Code Online (Sandbox Code Playgroud)

一旦我删除ESCAPE它再次开始使用索引,查询在60-70ms完成.

UPDATE

结果如下.

EXPLAIN QUERY PLAN
SELECT * FROM [RegistryValues]
WHERE
     [ValueName] LIKE 'windir%' ESCAPE '\' 
Run Code Online (Sandbox Code Playgroud)

SCAN TABLE RegistryValues (~3441573 rows)

而那个没有 ESCAPE

EXPLAIN QUERY PLAN
SELECT * FROM [RegistryValues]
WHERE
     [ValueName] LIKE 'windir%'
Run Code Online (Sandbox Code Playgroud)

SEARCH TABLE RegistryValues USING INDEX IdxRegistryValuesValueNameKeyIdKeyHiveFileId (ValueName>? AND ValueName<?) (~31250 rows)

UPDATE

刚发现这个

http://www.sqlite.org/optoverview.html

4.0 LIKE优化

The ESCAPE clause cannot appear on the LIKE operator

那我该怎么办呢?

我理解对吗?我无法使用LIKE运算符搜索包含通配符的字符串SQLite.我的意思是说通配符_ % ^ !

这是不可能的,因为我无法摆脱他们.实际上我可以,但在这种情况下我不能使用索引,因此查询效率不高.

我对吗?

CL.*_*CL. 6

索引只能在末尾处使用LIKE子句,%以便SQLite可以将其重写为两个简单的比较(如EXPLAIN输出中所示).

因此,要获得相同的效果,请自己编写比较.这要求您构造一些保证比任何匹配值"更大"的字符串(谨防非ASCII字符).代替:

... WHERE Name LIKE 'Text%'
Run Code Online (Sandbox Code Playgroud)

使用:

... WHERE Name BETWEEN 'Text' AND 'Textzzzzzzzzzzzzz'
Run Code Online (Sandbox Code Playgroud)

或者,作为参数:

... WHERE Name BETWEEN @Pattern AND @Pattern || 'zzzzzzzzzz'
Run Code Online (Sandbox Code Playgroud)

(这个结构永远不需要逃脱.:)