Postgres中LIKE和〜之间的区别

Cel*_*tas 56 regex sql postgresql syntax

我被指示"不要打扰LIKE"并~改为使用.什么是错的LIKE又是怎样的~不同?

~在这种情况下是否有名称或者人们说"使用代字号运算符"?

ast*_*asr 47

~是正则表达式运算符,并具有隐含的功能.您可以指定全范围的正则表达式通配符和量词; 有关详细信息,请参阅文档 当然LIKE,它需要更强大的功能,并且应该在需要功率时使用,但它们可以用于不同的目的.

  • 这是非常糟糕的建议.每当你可以使用SQL标准`LIKE`时,它通常比正则表达式更可取.使用适当的索引更简单,更快速,更容易.正则表达式更强大,但速度更慢且非标准. (28认同)
  • 这就是为什么我说"在适当的时候,但它们用于不同的目的." (12认同)
  • `在适当的情况下,应优先使用LIKE运算符.如果您有选择,请使用LIKE.当你没有选择时,你无论如何都没有选择. (10认同)
  • 相应地删除了downvote,因为答案不再让我感到错误. (3认同)
  • 你有没有看到有人试图将模式匹配变成具有多个LIKE子句的查询?这在其他一些系统中是必要的,但Postgres提供了正则表达式运算符.也许我应该说:"应该优先使用多个LIKE子句." (2认同)
  • 编辑以反映这一讨论. (2认同)

Cra*_*ger 33

LIKE和IMO没有任何不妥,没有理由支持~它.恰恰相反.LIKE是SQL标准.所以SIMILAR TO,但它没有得到广泛支持.PostgreSQL ~ operator(或posix正则表达式匹配运算符)不是SQL标准.

出于这个原因,我更喜欢使用LIKE足够表达的地方而我只~在我需要完整正则表达式的力量时使用.如果我需要移植数据库,那么就会受到一点伤害.我已经倾向于使用SIMILAR TO什么时候LIKE不够强大,但在Erwin的评论之后,我想我会停止这样做,并~LIKE不做的时候使用.

此外,PostgreSQL的可以使用前缀搜索(例如B-tree索引LIKE 'TEST%')用LIKESIMILAR TO如果数据库是在C区域或索引有text_pattern_ops.与我之前写的相反,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);
Run Code Online (Sandbox Code Playgroud)

请注意,~使用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)
Run Code Online (Sandbox Code Playgroud)


Erw*_*ter 28

PostgreSQL中有许多模式匹配运算符.LIKE,SIMILAR TO~ 在本手册页中介绍.

如果可以的话,使用LIKE(~~),它是最快的.
如果你不能,使用正则表达式(~),它会更强大.
绝不是用户SIMILAR TO.这完全没有意义.更进一步.

安装附加模块pg_trgm也可以使相似性运算符%可用.

为了使图片完整,还有自己的基础设施进行文本搜索.

每个运营商都可以获得不同程度的索引支持.它经常胜过其他选择的表现.但即使有索引,细节也有很多余地.

没有pg_trgm,左锚定搜索模式有索引支持.如果你的数据库集群与非"C"语言环境中运行,你需要一个指数具有特殊的运算符类,如@@text_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
Run Code Online (Sandbox Code Playgroud)

SQL小提琴.

安装了pg_trgm后,您还可以选择将GIN或GiST索引与运算符类varchar_pattern_ops或使用gist_trgm_ops.这些索引可以支持任何 gin_trgm_ops表达式,而不仅仅是左侧锚定.但是,还没有正则表达式的支持(除了基本的左锚定之外).亚历山大·科罗特科夫和其他人正在努力:

更新:在Postgres 9.3添加了对任意正则表达式匹配的支持,并且自那以后进行了多次改进.

LIKE是SQL标准的一部分,但它是非常奇怪的语法,并且PostgreSQL支持它的唯一原因是保持标准兼容.在内部,每个SIMILAR TO表达式都用正则表达式重写.因此,对于任何给定的SIMILAR TO表达式,至少有一个正则表达式可以更快地完成相同的工作.我从不使用SIMILAR TO.更多:

  • 我最近读到,无论如何,可能会从ANSI SQL标准的下一版本(有利于REGEXP_LIKE)中删除`SIMILAR TO`.而且我不确定使用`SIMILAR TO`超过`~`的可移植性是否值得,因为其他DBMS(Oracle,SQL Server,MySQL)不支持该标准. (3认同)

小智 7

~~操作相当于LIKE. ~另一方面,将使用POSIX正则表达式进行匹配.

  • 只是一个意见(以及评论),但我不喜欢``~~``运算符,因为它是Postgres特定的并且失去了SQL可移植性而没有真正的收益. (3认同)
  • @syrion:很抱歉不清楚,我完全同意 - 我只是说`~`与`LIKE'没有做同样的事情. (3认同)

Ale*_*lex 6

我只是做了一个快速而简单的基准测试,以便在没有涉及索引时查看两个运算符之间的性能差异:

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
Run Code Online (Sandbox Code Playgroud)

在这个例子中,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)
Run Code Online (Sandbox Code Playgroud)

  • 哇,这是一个巨大的差异.好例子! (2认同)
  • 你能否提供解释计划,看看三角洲的解释是否与克雷格林格上面的解释一致? (2认同)

小智 6

Like 只是在开头或 End 或 Middle 匹配字符串的一部分并且倾斜 (~) 与正则表达式匹配

为了进一步解释这一点,让我们创建一个表并插入一些值

# create table users(id serial primary key, name character varying);
Run Code Online (Sandbox Code Playgroud)

现在让我们在表中插入一些值

# insert into users (name) VALUES ('Alex'), ('Jon Snow'), ('Christopher'), ('Arya'),('Sandip Debnath'), ('Lakshmi'),('alex@gmail.com'),('@sandip5004'), ('lakshmi@gmail.com');
Run Code Online (Sandbox Code Playgroud)

现在你的桌子应该是这样的

 id |       name        
----+-------------------
  1 | Alex
  2 | Jon Snow
  3 | Christopher
  4 | Arya
  5 | Sandip Debnath
  6 | Lakshmi
  7 | alex@gmail.com
  8 | lakshmi@gmail.com
  9 | @sandip5004
Run Code Online (Sandbox Code Playgroud)

案例喜欢

# select * from users where name like 'A%';
 id | name 
----+------
  1 | Alex
  4 | Arya
(2 rows)
Run Code Online (Sandbox Code Playgroud)

如您所见, 'A%'我们只会得到名称以大写字母 A 开头的值。

# select * from users where name like '%a%';
 id |       name        
----+-------------------
  4 | Arya
  5 | Sandip Debnath
  6 | Lakshmi
  7 | alex@gmail.com
  8 | lakshmi@gmail.com
Run Code Online (Sandbox Code Playgroud)

正如您所看到的, '%a%'只会为我们获取名称a介于名称之间的值。

# select * from users where name like '%a';

 id | name 
----+------
  4 | Arya
Run Code Online (Sandbox Code Playgroud)

如您所见, '%a'我们只会获取名称以a.

案例~(倾斜)

# select * from users where name ~* 't';
 id |      name      
----+----------------
  3 | Christopher
  5 | Sandip Debnath
Run Code Online (Sandbox Code Playgroud)

正如您所看到的, name ~* 't'只会为我们获取名称为t. ~表示区分大小写, ~* 表示不区分大小写,所以

# select * from users where name ~ 'T';
 id | name 
----+------
(0 rows)
Run Code Online (Sandbox Code Playgroud)

上面的查询给了我们 0 行,因为T与任何条目都不匹配

现在让我们考虑一种情况,我们只需要获取电子邮件 ID,我们不知道邮件 ID 有什么,但我们知道电子邮件的模式,即会有一些字母或数字或 _ 或 。或 - 然后是@,然后是更多的字母或数字,或 - 然后。然后cominorg etc我们可以使用正则表达式创建模式。

现在让我们尝试使用正则表达式获取结果

# select * from users where name ~* '[a-z0-9\.\-\_]+@[a-z0-9\-]+\.[a-z]{2,5}';
 id |       name        
----+-------------------
  7 | alex@gmail.com
  8 | lakshmi@gmail.com
Run Code Online (Sandbox Code Playgroud)

同样,我们可以获取一些中间有空格的名称

#select * from users where name ~* '[a-z]+\s[a-z]+';
 id |      name      
----+----------------
  2 | Jon Snow
  5 | Sandip Debnath
Run Code Online (Sandbox Code Playgroud)

[az]+ 表示可以有从 a 到 z 的任何字母,+ 表示它可能出现 1 次或多次,\s 表示之后会有一个空格,然后又是一组可以出现 1 次或多次的字母次。

希望这个详细的分析有帮助。