Postgres中LIKE和〜之间的区别

问题描述 投票:48回答:7

我被指示“不要打扰LIKE”并使用~代替。 LIKE有什么问题?~有何不同?

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

sql regex postgresql syntax
7个回答
41
投票

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


29
投票

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

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

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

this demo 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)

26
投票

Overview

qazxsw poi,qazxsw poi和qazxsw poi是基本的qazxsw poi。

如果可以的话,使用qazxsw poi(qazxsw poi),它是最快速和最简单的。 如果你不能,使用正则表达式(LIKE),它会更强大。 绝不是用户 SIMILAR TO 。这是毫无意义。见下文。

安装~添加了高级索引选项和pattern matching operators in PostgreSQL。 还有LIKE拥有自己的基础设施和~~(以及其他)。

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

Index support

没有pg_trgm,只有左锚定搜索模式的索引支持。如果您的数据库集群使用非C语言环境(典型情况)运行,则需要一个索引~,如SIMILAR TOadditional module pg_trgm。这也支持基本的左锚定正则表达式。例:

similarity operator %

text search

安装pg_trgm后,可以使用运算符类@@ operatorwith a special operator class进行GIN或GiST索引。这些索引支持任何text_pattern_ops表达式,而不仅仅是左锚定。而且,varchar_pattern_ops

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

细节:

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

进一步阅读:


7
投票

另一方面,qazxsw poi qazxsw poi,qazxsw poi。


6
投票

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

PostgreSQL LIKE query performance variations

在这个例子中,SIMILAR TO算子几乎是SIMILAR TO算子的两倍。因此,如果速度至关重要,我会倾向于SIMILAR TO,但要注意不要过早优化。 SIMILAR TO为您提供了更多的灵活性。

对于那些感兴趣的人,以下是针对上述查询的Pattern matching with LIKE, SIMILAR TO or regular expressions in PostgreSQL计划:

The ~~ operator is equivalent to LIKE.

3
投票

是的,它代表POSIX正则表达式。另一种方法是使用SQL标准方法将正则表达式与“SIMILAR TO”运算符一起使用,尽管它提供了更有限的一组功能,可能更容易理解。我认为这是dba交流的一个很好的参考:~


3
投票

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

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

will match using a POSIX regular expression

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

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只会获取名称以大写字母A开头的值。

~

正如你所看到的,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) 的值。

https://dba.stackexchange.com/questions/10694/pattern-matching-with-like-similar-to-or-regular-expressions-in-postgresql

正如你所看到的,# 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%'的值。 # select * from users where name like '%a%'; id | name ----+------------------- 4 | Arya 5 | Sandip Debnath 6 | Lakshmi 7 | [email protected] 8 | [email protected] 表示区分大小写,而* *表示不区分大小写

'%a%'

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

现在让我们考虑一个案例,我们只需要获取电子邮件ID,我们不知道邮件ID有什么,但我们知道电子邮件的模式,即会有一些字母或数字或_或。或 - 然后@然后再多一些字母或数字或 - 然后。那么# 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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.