Мне было дано указание "не беспокоиться с LIKE
" и вместо этого используйте ~
. Что не так с LIKE
и чем отличается ~
?
Имеет ли ~
имя в этом контексте или люди говорят "использовать оператор тильды"?
Мне было дано указание "не беспокоиться с LIKE
" и вместо этого используйте ~
. Что не так с LIKE
и чем отличается ~
?
Имеет ли ~
имя в этом контексте или люди говорят "использовать оператор тильды"?
~
является оператором регулярного выражения и имеет возможности, связанные с этим. Вы можете указать полный диапазон подстановочных знаков и квантификаторов регулярного выражения; см. Раздел 9.7.3 документации. Это, безусловно, более мощное, чем LIKE
, и должно использоваться, когда эта мощность необходима, но они служат различным целям.
Нет ничего плохого в LIKE
и, IMO, нет причин одобрять ~
над ним. Скорее наоборот. LIKE
является стандартом SQL. Так что SIMILAR TO
, но он не поддерживается широко. PostgreSQL ~ operator
(или оператор сопоставления регулярных выражений posix) не является стандартом SQL.
По этой причине я предпочитаю использовать LIKE
, где он достаточно экспрессивный, и я использую только ~
, когда мне нужна сила полных регулярных выражений. Если мне когда-нибудь понадобится порт баз данных, это будет меньше всего, что будет больно. Я обычно использовал SIMILAR TO
, когда LIKE
недостаточно мощный, но после комментариев Erwin я думаю, что перестану это делать и использую ~
, когда LIKE
не выполняет эту работу.
Кроме того, PostgreSQL может использовать индекс b-дерева для поиска префикса (например, LIKE 'TEST%'
) с помощью LIKE
или SIMILAR TO
, если база данных находится в локали C
или индекс имеет text_pattern_ops
. Вопреки тому, что я написал ранее, Pg также может использовать такой индекс для левого привязанного rexx posix, ему просто нужен явный '^ TEST. *', Поэтому регулярное выражение может только совпадать с самого начала. В моем сообщении ранее неверно указано, что ~
не может использовать индекс для поиска префикса. С той разницей, что это действительно исключено, нужно ли придерживаться стандартных совместимых функций там, где это возможно или нет.
Смотрите эту демонстрацию SQLFiddle; обратите внимание на различные планы выполнения. Обратите внимание на разницу между ~ '1234.*'
и ~ '^1234.*'
.
Данные образца:
create table test (
blah text
);
insert into test (blah) select x::text from generate_series(1,10000) x;
create index test_blah_txtpat_idx ON test(blah text_pattern_ops);
обратите внимание, что ~
использует seqscan, даже если он значительно дороже (это из-за enable_seqscan
), потому что у него нет альтернативы, а LIKE
использует индекс. Однако исправленный ~
с левым якорем также использует индекс:
regress=# SET enable_seqscan = 'f';
SET
regress=# explain select 1 from test where blah ~ '12.*';
QUERY PLAN
---------------------------------------------------------------------------
Seq Scan on test (cost=10000000000.00..10000000118.69 rows=2122 width=0)
Filter: (blah ~ '12.*'::text)
(2 rows)
regress=# explain select 1 from test where blah like '12%';
QUERY PLAN
------------------------------------------------------------------------------------
Bitmap Heap Scan on test (cost=4.55..46.76 rows=29 width=0)
Filter: (blah ~~ '12%'::text)
-> Bitmap Index Scan on test_blah_txtpat_idx (cost=0.00..4.54 rows=29 width=0)
Index Cond: ((blah ~>=~ '12'::text) AND (blah ~<~ '13'::text))
(4 rows)
regress=# explain select 1 from test where blah ~ '^12.*';
QUERY PLAN
-------------------------------------------------------------------------------------
Bitmap Heap Scan on test (cost=5.28..51.53 rows=101 width=0)
Filter: (blah ~ '^12.*'::text)
-> Bitmap Index Scan on test_blah_txtpat_idx (cost=0.00..5.25 rows=100 width=0)
Index Cond: ((blah ~>=~ '12'::text) AND (blah ~<~ '13'::text))
(4 rows)
В PostgreSQL существует ряд операторов сопоставления шаблонов. LIKE
, SIMILAR TO
и ~
описаны на этой странице руководства.
Если вы можете, используйте LIKE
(~~
), это самый быстрый.
Если вы не можете, используйте регулярное выражение (~
), оно более мощное.
Никогда пользователь . Это совершенно бессмысленно. Еще ниже.SIMILAR TO
Установка дополнительного модуля pg_trgm также делает оператор подобия %
.
Чтобы сделать изображение полным, есть также текстовый поиск с его собственной инфраструктурой.
Поддержка индексов доступна для каждого из этих операторов - в разной степени. Он регулярно превосходит производительность других опций. Но в деталях есть много возможностей, даже с индексами.
Без pg_trgm существует поддержка индексов для левых привязанных шаблонов поиска. Если ваш кластер баз данных работает с языковой версией "C", вам нужен индекс со специальным классом оператора, например text_pattern_ops
или varchar_pattern_ops
. И да, поддерживаются основные левые привязанные регулярные выражения.
Пример:
CREATE TABLE tbl(string text);
INSERT INTO tbl(string)
SELECT x::text FROM generate_series(1, 10000) x;
CREATE INDEX tbl_string_text_pattern_idx ON tbl(string text_pattern_ops);
SELECT * FROM tbl WHERE string ~ '^1234'; -- left anchored pattern
Установив pg_trgm, вы также можете использовать индексы GIN или GiST с классами операторов gist_trgm_ops
или gin_trgm_ops
. Эти индексы могут поддерживать любое выражение LIKE
, а не только привязанные слева. Однако пока нет поддержки регулярных выражений (кроме базовых лево-привязанных). Александр Коротков и другие работают над этим:
Обновление. Поддержка произвольных совпадений регулярных выражений была добавлена в Postgres 9.3 и улучшена несколько раз с тех пор.
SIMILAR TO
является частью стандарта SQL, но это очень странный синтаксис, и единственная причина, по которой PostgreSQL поддерживает его, заключается в том, чтобы оставаться стандартным. Внутренне каждое выражение SIMILAR TO
переписывается с регулярным выражением. Поэтому для любого заданного выражения SIMILAR TO
существует по крайней мере одно регулярное выражение, выполняющее ту же работу быстрее. Я никогда не использую SIMILAR TO
. Подробнее:
Оператор ~~
эквивалентен LIKE
. ~
, с другой стороны, будет соответствовать используя регулярное выражение POSIX.
Я просто сделал быстрый и простой тест, чтобы посмотреть на разницу в производительности между двумя операторами, когда не задействованы никакие индексы:
postgres=# \timing
Timing is on.
postgres=# SELECT count(1) FROM (SELECT val from generate_series(1, 10000000) x(val) WHERE val::text LIKE '%5%') AS x;
count
─────────
5217031
(1 row)
Time: 5631.662 ms
postgres=# SELECT count(1) FROM (SELECT val from generate_series(1, 10000000) x(val) WHERE val::text ~ '5') AS x;
count
─────────
5217031
(1 row)
Time: 10612.406 ms
В этом примере оператор LIKE
почти в два раза быстрее, чем оператор ~
. Поэтому, если скорость имеет смысл, я бы наклонился к LIKE
, хотя будьте осторожны, чтобы не доработать преждевременно. ~
дает вам большую гибкость.
Для тех из вас, кто заинтересован, вот EXPLAIN
планы для вышеуказанных запросов:
postgres=# EXPLAIN ANALYZE SELECT count(1) FROM (SELECT val from generate_series(1, 10000000) x(val) WHERE val::text LIKE '%5%') AS x;
QUERY PLAN
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Aggregate (cost=20.00..20.01 rows=1 width=0) (actual time=9967.748..9967.749 rows=1 loops=1)
-> Function Scan on generate_series x (cost=0.00..17.50 rows=1000 width=0) (actual time=1732.084..7404.755 rows=5217031 loops=1)
Filter: ((val)::text ~~ '%5%'::text)
Rows Removed by Filter: 4782969
Total runtime: 9997.587 ms
(5 rows)
postgres=# EXPLAIN ANALYZE SELECT count(1) FROM (SELECT val from generate_series(1, 10000000) x(val) WHERE val::text ~ '5') AS x;
QUERY PLAN
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Aggregate (cost=20.00..20.01 rows=1 width=0) (actual time=15118.061..15118.061 rows=1 loops=1)
-> Function Scan on generate_series x (cost=0.00..17.50 rows=1000 width=0) (actual time=1724.591..12516.996 rows=5217031 loops=1)
Filter: ((val)::text ~ '5'::text)
Rows Removed by Filter: 4782969
Total runtime: 15147.950 ms
(5 rows)
Да, это означает регулярное выражение POSIX. Другой альтернативой является использование стандартного подхода SQL к регулярным выражениям с оператором "SIMILAR TO", хотя он предоставляет более ограниченный набор функций, может быть проще понять. Я думаю, что это хорошая ссылка из обмена dba: https://dba.stackexchange.com/info/10694/pattern-matching-with-like-similar-to-or-regular-expressions-in-postgresql