「LIKE
に煩わされないように」と指示され、~
代わりに。 LIKE
の問題点と~
異なる?
~
このコンテキストで名前を持っているか、人々は「チルダ演算子を使用する」と言いますか?
~
は正規表現演算子であり、それによって暗示される機能を備えています。正規表現のワイルドカードと数量詞の全範囲を指定できます。詳細については、 ドキュメント を参照してください。確かにLIKE
より強力であり、そのパワーが必要なときに使用されるべきですが、それらは異なる目的に役立ちます。
LIKE
に問題はありません。IMOには、~
を優先する理由はありません。むしろ反対。 LIKE
はSQL標準です。 SIMILAR TO
も同様ですが、広くサポートされていません。 PostgreSQLの ~ operator
(またはposix正規表現一致演算子)はSQL標準ではありません。
そのため、十分に表現力のあるLIKE
を使用することを好み、完全な正規表現の力が必要な場合にのみ~
を使用します。データベースを移植する必要が生じたとしても、それはもう1つ少なくなります。 LIKE
が十分に強力でない場合はSIMILAR TO
を使用する傾向がありましたが、Erwinのコメントの後、それをやめ、LIKE
が機能しない場合は~
を使用すると思います。
また、PostgreSQLは、データベースがLIKE
ロケールにあるか、インデックスにLIKE 'TEST%'
がある場合、C
またはSIMILAR TO
を使用したプレフィックス検索(たとえばtext_pattern_ops
)にbツリーインデックスを使用できます。前に書いたものとは逆に、Pgは左アンカーの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);
LIKE
はインデックスを使用しますが、~
は代替手段がないため、かなり高価な場合でも(enable_seqscan
が原因で)seqscanを使用することに注意してください。ただし、左アンカー付きの修正済み~
は、インデックスも使用します。
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)
LIKE
、SIMILAR TO
、および~
は、基本的な PostgreSQLのパターンマッチング演算子 です。
可能であれば、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
Withpg_trgmをインストールすると、演算子クラスGist_trgm_ops
またはgin_trgm_ops
を使用してGINまたはGistインデックスが可能になります。これらのインデックスは、左アンカーだけでなく、anyLIKE
式をサポートします。そして、 マニュアルを引用:
PostgreSQL 9.3以降、これらのインデックスタイプは正規表現一致のインデックス検索もサポートします。
詳細:
SIMILAR TO
は非常に奇妙な構造です。 PostgreSQLは、SQL標準の初期バージョンで定義されたため、これを実装するだけです。内部的に、すべてのSIMILAR TO
式は正規表現で書き換えられます。したがって、特定のSIMILAR TO
式には、同じジョブを実行する少なくとも1つの正規表現がありますfaster。 I neverSIMILAR TO
を使用します。
参考文献:
2つの演算子のパフォーマンスの違いを調べるために、すばやく簡単なベンチマークを行いましたインデックスが含まれていない場合:
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
演算子は~
演算子のほぼ2倍の速度です。したがって、速度が重要な場合は、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)
同様に、文字列の先頭または末尾または中間のいずれかに文字列の一部が一致するだけで、傾き(〜)は正規表現に一致します
これをさらに説明するために、テーブルを作成していくつかの値を挿入しましょう
# create table users(id serial primary key, name character varying);
次に、テーブルにいくつかの値を挿入しましょう
# insert into users (name) VALUES ('Alex'), ('Jon Snow'), ('Christopher'), ('Arya'),('Sandip Debnath'), ('Lakshmi'),('[email protected]'),('@sandip5004'), ('[email protected]');
これで、テーブルは次のようになります。
id | name
----+-------------------
1 | Alex
2 | Jon Snow
3 | Christopher
4 | Arya
5 | Sandip Debnath
6 | Lakshmi
7 | [email protected]
8 | [email protected]
9 | @sandip5004
# select * from users where name like 'A%';
id | name
----+------
1 | Alex
4 | Arya
(2 rows)
ご覧のように 'A%'
は、名前が大文字のAで始まる値のみを取得します。
# select * from users where name like '%a%';
id | name
----+-------------------
4 | Arya
5 | Sandip Debnath
6 | Lakshmi
7 | [email protected]
8 | [email protected]
ご覧のように '%a%'
は、名前の間にa
を含む名前の値のみを取得します。
# select * from users where name like '%a';
id | name
----+------
4 | Arya
ご覧のように '%a'
は、名前がa
で終わる値のみを取得します。
# select * from users where name ~* 't';
id | name
----+----------------
3 | Christopher
5 | Sandip Debnath
ご覧のように name ~* 't'
は、名前にt
を持つ値のみを取得します。 ~
は大文字と小文字を区別し、〜*は大文字と小文字を区別しないことを意味します。
# select * from users where name ~ 'T';
id | name
----+------
(0 rows)
T
はどのエントリとも一致しなかったため、上記のクエリでは0行が返されました。
ここで、メールIDを取得するだけで、メールIDが何であるかがわからない場合を考えてみましょう。メールのパターンはわかっています。つまり、文字や数字、または_またはがあります。または-次に@、さらに文字または数字、または-次にcom
またはin
またはorg
etc
を使用して、正規表現を使用してパターンを作成できます。
正規表現を使用して結果を取得してみましょう
# select * from users where name ~* '[a-z0-9\.\-\_]+@[a-z0-9\-]+\.[a-z]{2,5}';
id | name
----+-------------------
7 | [email protected]
8 | [email protected]
同様に、間にスペースがあるいくつかの名前を取得できます
#select * from users where name ~* '[a-z]+\s[a-z]+';
id | name
----+----------------
2 | Jon Snow
5 | Sandip Debnath
[az] +はaからzまでの任意の文字があることを意味し、+は1回以上出現する可能性があることを意味し、\ sはその後にスペースがあり、1回以上出現する可能性のある文字セットを意味する回。
この詳細な分析が役に立てば幸いです。
はい、POSIX正規表現の略です。もう1つの代替方法は、SQL標準のアプローチを使用して「類似」演算子を使用した正規表現を使用することです。ただし、より限定された機能セットを提供しますが、理解しやすい場合があります。これはdba exchangeからの良いリファレンスだと思います: https://dba.stackexchange.com/questions/10694/pattern-matching-with-like-similar-to-or-regular-expressions-in-postgresql