Мне пришлось написать простой запрос, где я иду искать имена людей, которые начинаются с B или D:
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
Мне было интересно, есть ли способ переписать это, чтобы стать более производительным. Так что я могу избежать or
и / или like
?
postgresql
performance
index
regular-expression
pattern-matching
Лукас Кауфман
источник
источник
s.name
индексироваться?name
может быть полезен здесь, если вы заботитесь о производительности.Ответы:
Ваш запрос в значительной степени оптимальный. Синтаксис не станет намного короче, запрос не станет намного быстрее:
Если вы действительно хотите сократить синтаксис , используйте регулярное выражение с ветвями :
Или немного быстрее, с классом персонажа :
Быстрый тест без индекса дает более быстрые результаты, чем
SIMILAR TO
в любом случае для меня.С соответствующим индексом B-Tree
LIKE
выигрывает эту гонку на порядки.Прочитайте основы сопоставления с образцом в руководстве .
Индекс для превосходной производительности
Если вас интересует производительность, создайте такой индекс для больших таблиц:
Делает такой запрос быстрее на порядок. Особые соображения применяются для порядка сортировки, зависящего от локали. Подробнее о классах операторов читайте в руководстве . Если вы используете стандартную локализацию "C" (большинство людей этого не делают), подойдет простой индекс (с классом оператора по умолчанию).
Такой индекс хорош только для левых якорных паттернов (совпадающих с начала строки).
SIMILAR TO
или регулярные выражения с базовыми левосторонними выражениями также могут использовать этот индекс. Но не с ветвями(B|D)
или классами символов[BD]
(по крайней мере, в моих тестах на PostgreSQL 9.0).Для совпадения триграмм или текстового поиска используются специальные индексы GIN или GiST.
Обзор операторов сопоставления с образцом
LIKE
(~~
) прост и быстр, но ограничен в своих возможностях.ILIKE
(~~*
) регистронезависимый вариант.pg_trgm расширяет поддержку индекса для обоих.
~
(совпадение регулярного выражения) является мощным, но более сложным и может быть медленным для чего-то большего, чем базовые выражения.SIMILAR TO
просто бессмысленно . Своеобразный полукровкаLIKE
и регулярные выражения. Я никогда не использую это. Смотри ниже.% - это оператор «сходства», предоставляемый дополнительным модулем
pg_trgm
. Смотри ниже.@@
является оператором текстового поиска. Смотри ниже.pg_trgm - сопоставление триграмм
Начиная с PostgreSQL 9.1, вы можете упростить расширение,
pg_trgm
чтобы обеспечить поддержку индексов для любогоLIKE
/ILIKE
шаблона (и простых шаблонов регулярных выражений с~
) с использованием индекса GIN или GiST.Подробности, пример и ссылки:
pg_trgm
также предоставляет следующие операторы :%
- оператор "сходства"<%
(commutator%>
:) - оператор "word_s Similarity" в Postgres 9.6 или более поздней версии<<%
(commutator%>>
:) - оператор "strict_word_simility" в Postgres 11 или более поздней версииПоиск текста
Это специальный тип сопоставления с шаблоном с отдельными типами инфраструктуры и индекса. Он использует словари и основы и является отличным инструментом для поиска слов в документах, особенно для естественных языков.
Сопоставление префиксов также поддерживается:
Так же как поиск фразы начиная с Postgres 9.6:
Рассмотрим введение в руководство и обзор операторов и функций .
Дополнительные инструменты для нечеткого соответствия строк
Дополнительный модуль fuzzystrmatch предлагает еще несколько опций, но производительность, как правило, уступает всем вышеперечисленным.
В частности, различные реализации
levenshtein()
функции могут быть инструментальными.Почему регулярные выражения (
~
) всегда быстрее чемSIMILAR TO
?Ответ прост.
SIMILAR TO
выражения переписываются внутри регулярных выражений. Таким образом, для каждогоSIMILAR TO
выражения есть хотя бы одно более быстрое регулярное выражение (которое позволяет сэкономить на переписывании выражения). Там нет прирост производительности при использованииSIMILAR TO
никогда .А простые выражения, которые можно сделать с помощью
LIKE
(~~
), вLIKE
любом случае быстрее .SIMILAR TO
поддерживается только в PostgreSQL, потому что он оказался в ранних версиях стандарта SQL. Они до сих пор не избавились от этого. Но есть планы удалить его и включить вместо него совпадения регулярных выражений - или я так слышал.EXPLAIN ANALYZE
раскрывает это. Попробуйте сами с любым столом!раскрывает:
SIMILAR TO
был переписан с помощью регулярного выражения (~
).Максимальная производительность в данном конкретном случае
Но
EXPLAIN ANALYZE
раскрывает больше. Попробуйте, с указанным выше индексом:раскрывает:
Внутренне, с индексом , который не зависит от локали известно (
text_pattern_ops
или с помощью локалиC
) простые выражения лево-якорь переписываются с этими операторами текста шаблона:~>=~
,~<=~
,~>~
,~<~
. Это случай~
,~~
илиSIMILAR TO
как.То же самое верно для индексов
varchar
типов сvarchar_pattern_ops
илиchar
сbpchar_pattern_ops
.Итак, применительно к исходному вопросу, это самый быстрый способ :
Конечно, если вам придется искать соседние инициалы , вы можете упростить дальнейшее:
Выгода по сравнению с простым использованием
~
или~~
крошечная. Если производительность не является вашим главным требованием, вам следует просто придерживаться стандартных операторов - прийти к тому, что у вас уже есть в вопросе.источник
similar
поиска диапазона и сканирование?EXPLAIN ANALYZE
просмотром 2-х растровых индексов. Многократное сканирование индекса растрового изображения может быть объединено довольно быстро.OR
сUNION ALL
или заменname LIKE 'B%'
сname >= 'B' AND name <'C'
в Postgres?UNION
не будет, но да, объединение диапазонов в одномWHERE
предложении ускорит запрос. Я добавил больше к своему ответу. Конечно, вы должны принять во внимание ваш язык. Поиск с учетом локали всегда медленнее.Как насчет добавления столбца в таблицу. В зависимости от ваших реальных требований:
PostgreSQL не поддерживает вычисляемые столбцы в базовых таблицах в SQL Server, но новый столбец можно поддерживать с помощью триггера. Очевидно, этот новый столбец будет проиндексирован.
Кроме того, индекс по выражению даст вам то же самое, дешевле. Например:
Запросы, которые соответствуют выражению в их условиях, могут использовать этот индекс.
Таким образом, снижение производительности происходит при создании или изменении данных, поэтому может подходить только для среды с низкой активностью (т. Е. Гораздо меньше операций записи, чем операций чтения).
источник
Вы могли бы попробовать
Я понятия не имею, могут ли вышеперечисленное или ваше оригинальное выражение саргитировать в Postgres.
При создании предложенного индекса было бы также интересно услышать, как это соотносится с другими вариантами.
источник
То, что я сделал в прошлом, столкнувшись с похожей проблемой производительности, это увеличил ASCII-символ последней буквы и сделал МЕЖДУ. Затем вы получите лучшую производительность для подмножества функциональности LIKE. Конечно, это работает только в определенных ситуациях, но для сверхбольших наборов данных, где вы ищете, например, по имени, производительность снижается от ужасной до приемлемой.
источник
Очень старый вопрос, но я нашел другое быстрое решение этой проблемы:
Так как функция ascii () смотрит только на первый символ строки.
источник
(name)
?Для проверки инициалов я часто использую приведение к
"char"
(с двойными кавычками). Это не портативный, но очень быстрый. Внутренне он просто удаляет текст и возвращает первый символ, и операции сравнения типа «char» выполняются очень быстро, поскольку тип имеет фиксированную длину в 1 байт:Обратите внимание, что приведение к
"char"
выполняется быстрее, чемascii()
slution @ Sole021, но оно не совместимо с UTF8 (или любой другой кодировкой в этом отношении), возвращая просто первый байт, поэтому его следует использовать только в тех случаях, когда сравнение выполняется с простым старым 7 -битные символы ASCII.источник
Есть два метода, которые еще не упомянуты для решения таких случаев:
частичный (или секционированный - если создается для полного диапазона вручную) индекс - наиболее полезен, когда требуется только подмножество данных (например, во время некоторого обслуживания или временного для некоторых отчетов):
разбиение самой таблицы (используя первый символ в качестве ключа разделения) - этот метод особенно стоит рассмотреть в PostgreSQL 10+ (менее болезненное разбиение) и 11+ (сокращение разделов во время выполнения запроса).
Более того, если данные в таблице отсортированы, можно воспользоваться индексом BRIN (по первому символу).
источник
Вероятно, быстрее сделать сравнение одного символа:
источник
column LIKE 'B%'
будет более эффективным, чем использование функции подстроки в столбце.