Postgres中LIKE和〜之间的区别

时间:2012-09-17 02:11:32

标签: sql regex postgresql syntax

我被告知“不要打扰LIKE”并改为使用~LIKE有什么问题?~有何不同?

~在此上下文中是否有名称,或者有人说“使用代字号运算符”?

7 个答案:

答案 0 :(得分:41)

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

答案 1 :(得分:29)

LIKE和IMO没有理由赞成~。{恰恰相反。 LIKE是SQL标准。 SIMILAR TO也是如此,但它没有被广泛支持。 PostgreSQL的~ operator(或 posix正则表达式匹配运算符)不是SQL标准。

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

此外,如果数据库位于LIKE 'TEST%'语言环境或者LIKE语言环境中,PostgreSQL可以使用b-tree索引进行前缀搜索(例如SIMILAR TOCtext_pattern_ops index有~。与我之前写的相反,Pg也可以将这样的索引用于左锚定的posix正则表达式,它只需要一个显式的'^ TEST。*',因此正则表达式只能从头开始匹配。我之前的帖子错误地指出~ '1234.*'无法使用索引进行前缀搜索。消除了这种差异,这取决于您是否希望在可能的情况下坚持使用符合标准的功能。

this demo SQLFiddle;注意不同的执行计划。请注意~ '^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); 之间的差异。

给出样本数据:

~

请注意,enable_seqscan使用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) 也会使用索引:

{{1}}

答案 2 :(得分:26)

概述

LIKESIMILAR TO~是基本的pattern matching operators in PostgreSQL

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

安装additional module pg_trgm会添加高级索引选项和similarity operator % 还有text search有自己的基础设施和@@ operator(以及其他)。

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

索引支持

没有 pg_trgm ,只有左锚定搜索模式的索引支持。如果数据库集群使用非C语言环境(典型情况)运行,则需要索引with a special operator class,例如text_pattern_opsvarchar_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

SQL Fiddle.

安装 pg_trgm 后,可以使用运算符类gist_trgm_opsgin_trgm_ops进行GIN或GiST索引。这些索引支持任何 LIKE表达式,而不仅仅是左侧锚定。并且,quoting the manual:

  

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

详细说明:


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

进一步阅读:

答案 3 :(得分:7)

答案 4 :(得分: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)

答案 5 :(得分:3)

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

答案 6 :(得分:3)

就像只是匹配字符串的一部分,无论是在开头,结尾还是中间 倾斜(〜)与正则表达式匹配

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

# 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结尾的值。

Case〜(倾斜)

# 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次或多次。

希望此详细分析会有所帮助。