94

我被指示“不要打扰LIKE”并~改为使用。有什么问题,有什么LIKE不同~

在这种情况下是否~有名称或人们是否说“使用波浪号运算符”?

4

7 回答 7

74

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

于 2012-09-17T02:16:06.550 回答
44

没有任何问题,LIKE而且,IMO,没有理由赞成~它。而是相反。LIKE是 SQL 标准的。也是SIMILAR TO,但它没有得到广泛支持。PostgreSQL ~ operator(或posix 正则表达式匹配运算符)不是 SQL 标准。

出于这个原因,我更喜欢LIKE在表达能力足够的地方使用,并且我只~在需要完整正则表达式的功能时使用。如果我需要移植数据库,那就少了一件伤害的事情。我倾向于使用SIMILAR TOwhenLIKE不够强大,但在 Erwin 的评论之后,我想我会停止这样做并使用~whenLIKE不能完成这项工作。

此外,PostgreSQL 可以使用 b-tree 索引进行前缀搜索(例如LIKE 'TEST%'),LIKE如果SIMILAR 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);

请注意,~即使使用 seqscan 的成本要高得多(人为地因为 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)
于 2012-09-17T02:32:46.803 回答
40

概述

LIKE,SIMILAR TO并且是PostgreSQL~中的基本模式匹配运算符。

如果可以,请使用LIKE( ~~),它是最快和最简单的。
如果不能,请使用正则表达式 ( ~),它更强大。
从不用户SIMILAR TO. 这是毫无意义。见下文。

安装附加模块 pg_trgm添加高级索引选项和相似性运算符%
还有带有自己的基础设施和运营商(等等)的文本搜索。@@

这些运算符中的每一个都可以在不同程度上使用索引支持。它经常胜过其他选项的性能。但是在细节上还有很大的余地,即使有索引。

索引支持

如果没有pg_trgm,则只有左锚定搜索模式的索引支持。如果您的数据库集群使用非 C 语言环境(典型情况)运行,您需要一个带有特殊运算符类的索引,例如text_pattern_opsorvarchar_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

db<>在这里摆弄

安装pg_trgm,GIN 或 GiST 索引可以使用操作符类gist_trgm_opsgin_trgm_ops. 这些索引支持任何 LIKE表达式,而不仅仅是左锚定。并且,引用手册:

从 PostgreSQL 9.3 开始,这些索引类型也支持正则表达式匹配的索引搜索。

细节:


SIMILAR TO是一个非常奇怪的结构。PostgreSQL 只实现它,因为它是在 SQL 标准的早期版本中定义的。在内部,每个SIMILAR TO表达式都用正则表达式重写。因此,对于任何给定的表达式,至少有一个正则表达式可以更快SIMILAR TO地完成相同的工作。我从不使用.SIMILAR TO

进一步阅读:

于 2012-09-17T12:57:02.827 回答
7

运算符~~等价于。LIKE ~另一方面,将使用 POSIX 正则表达式进行匹配

于 2012-09-17T02:15:40.807 回答
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

在这个例子中,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)
于 2015-03-06T19:33:41.833 回答
6

Like 只是在开头或结尾或中间匹配字符串的一部分而倾斜 (~) 与正则表达式匹配

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

# create table users(id serial primary key, name character varying);

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

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

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

 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

案例喜欢

# 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 | alex@gmail.com
  8 | lakshmi@gmail.com

如您所见 '%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)

上面的查询给了我们 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

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

#select * from users where name ~* '[a-z]+\s[a-z]+';
 id |      name      
----+----------------
  2 | Jon Snow
  5 | Sandip Debnath

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

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

于 2018-08-30T15:15:05.577 回答
3

是的,它代表 POSIX 正则表达式。另一种选择是使用带有“SIMILAR TO”运算符的正则表达式的 SQL 标准方法,虽然它提供了更有限的一组功能,但可能更容易理解。我认为这是 dba 交换的一个很好的参考: https ://dba.stackexchange.com/questions/10694/pattern-matching-with-like-similar-to-or-regular-expressions-in-postgresql

于 2012-09-17T02:32:27.940 回答