Escape wildcards (%, _) в SQLite LIKE без ущерба для использования индекса?

У меня есть пара проблем с SQLite-запросом. На самом деле я начинаю думать, что SQLite не предназначен для таблиц с более чем 10 строками, на самом деле SQLite - это кошмар.

Следующий запрос

SELECT * FROM [Table] WHERE [Name] LIKE 'Text%'

Он отлично работает. EXPLAIN показывает, что индекс используется, и результат возвращается после 70ms.

Теперь мне нужно запустить этот запрос из драйвера .NET SQLite, поэтому я меняю запрос

SELECT * FROM [Table] WHERE [Name] LIKE @Pattern || '%'

Индекс не используется. Когда я запускаю следующий запрос в любом инструменте SQLite, индекс также не используется

SELECT * FROM [Table] WHERE [Name] LIKE 'Text' || '%'

Итак, я думаю, что у SQLite нет какой-либо логики предварительной обработки.

OK. Попробуем его решить, я все еще привязываю переменные и делаю следующее

SELECT * FROM [Table] WHERE [Name] LIKE @Pattern

Но теперь я добавляю символ % подстановочного символа в конец строки шаблона, например

command.Parameters.Add(new SQLiteParameter("@Pattern", pattern + '%'));

Он работает очень медленно. Я не могу сказать, почему, потому что, когда я запускаю этот запрос из инструмента SQLite, он отлично работает, однако, когда я привязываю эту переменную от .NET-кода, он работает медленно.

OK. Я все еще пытаюсь решить это. Я избавляюсь от привязки параметров шаблона и динамически строю это условие.

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

Индекс снова не используется. Он не используется из-за ESCAPE. Я вижу, что при запуске

EXPLAIN QUERY PLAN SELECT * FROM [Table] WHERE [Name] LIKE 'Text%' ESCAPE '\'

Как только я удаляю ESCAPE, он снова начинает использовать индекс, и запрос заканчивается в 60-70 мс.

UPDATE

Вот результаты.

EXPLAIN QUERY PLAN
SELECT * FROM [RegistryValues]
WHERE
     [ValueName] LIKE 'windir%' ESCAPE '\' 

SCAN TABLE RegistryValues (~3441573 rows)

а единица без ESCAPE

EXPLAIN QUERY PLAN
SELECT * FROM [RegistryValues]
WHERE
     [ValueName] LIKE 'windir%'

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. Говоря подстановочные знаки, я имею в виду _ % ^ !

Это невозможно просто потому, что я не могу избежать их. На самом деле я могу, но я не могу использовать индексы в этом случае, поэтому запрос не будет эффективным.

Я прав?

Ответ 1

Индекс может использоваться только с предложением LIKE, когда % находится в конце, поэтому SQLite может переписать его на два простых сравнения (как показано на выходе EXPLAIN).

Поэтому, чтобы получить тот же эффект, напишите сами сравнения. Это требует, чтобы вы построили некоторую строку, которая, как гарантируется, будет сравнивать "больше", чем любой из согласованных значений (остерегайтесь символов, отличных от ASCII). Вместо:

... WHERE Name LIKE 'Text%'

использование:

... WHERE Name BETWEEN 'Text' AND 'Textzzzzzzzzzzzzz'

или, как параметр:

... WHERE Name BETWEEN @Pattern AND @Pattern || 'zzzzzzzzzz'

(Эта конструкция никогда не нуждается в экранах.:)

Ответ 2

Убедитесь, что вы сверлите свои запросы в транзакции. Я делал некоторые запросы через библиотеку без нее, и добавив, что она увеличила скорость в 10 раз.

Вероятно, лучший ресурс по проблемам производительности: Улучшить производительность SQLite в SQL Server?