【问题标题】:Difference between LIKE and ~ in PostgresPostgres 中 LIKE 和 ~ 的区别
【发布时间】:2012-09-09 06:20:22
【问题描述】:

我被指示“不要打扰LIKE”,而是使用~LIKE 有什么问题,~ 有何不同?

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

【问题讨论】:

  • 受谁指导,出于兴趣?我很好奇。
  • @CraigRinger 课程讲师。
  • 明白了,谢谢。对不起,我只是说“哪里”而不是“谁”。

标签: sql regex postgresql syntax


【解决方案1】:

概述

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

db小提琴here

安装 pg_trgm 后,GIN 或 GiST 索引可以使用运算符类 gist_trgm_opsgin_trgm_ops。这些索引支持 any LIKE 表达式,而不仅仅是左锚定。还有,quoting the manual:

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

详情:


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

进一步阅读:

【讨论】:

  • 我最近读到SIMILAR TO 可能会从下一个版本的 ANSI SQL 标准中删除(支持 REGEXP_LIKE)。而且我不确定使用SIMILAR TO 而不是~ 在可移植性方面的收益是否值得,因为其他DBMS(Oracle、SQL Server、MySQL)不支持该标准。
  • @DavidFaber:我完全同意。
【解决方案2】:

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 有什么,但我们知道电子邮件的模式,即会有一些字母或数字或 _ 或 。或 - 然后是 @,然后是更多的字母或数字或 - 然后。然后com or in or org 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 次或多次。

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 这是一个非常糟糕的建议。只要您可以使用 SQL 标准LIKE,它通常比正则表达式更可取。使用适当的索引更简单、更快、更容易。正则表达式功能更强大,但速度较慢且不标准。
    • 这就是为什么我说“在适当的时候,但它们服务于不同的目的。”
    • should be used in preference to the LIKE operator when appropriate 是错误的。当您有选择时,请使用 LIKE。当你别无选择时,无论如何你都别无选择。
    • 你有没有看到有人试图将模式匹配硬塞到带有多个 LIKE 子句的查询中?这在其他一些系统中是必需的,但 Postgres 提供了 regex 运算符。也许我应该说:“应该优先使用多个 LIKE 子句。”
    • 相应地删除了反对票,因为答案不再让我觉得错了。
    【解决方案4】:

    我只是做了一个快速简单的基准测试来查看两个运算符之间的性能差异在不涉及索引时

    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)
    

    【讨论】:

    • 哇,这是一个巨大的差异。很好的例子!
    • 您能否提供解释计划,看看对 delta 的解释是否与 Craig Ringer 上面显示的解释一致?
    • 很好的建议,@eharik。完成。
    • 因此,使用像您在这里展示的那样的无索引测试数据集,您实际上只是在确认 Erwin 所说的话。即 ~~ 只是比 ~ 更快的运算符。
    【解决方案5】:

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

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

    此外,如果数据库位于C 语言环境中或索引具有text_pattern_ops,则PostgreSQL 可以使用带有LIKESIMILAR TO 的前缀搜索(例如LIKE 'TEST%')使用b-tree 索引。与我之前写的相反,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)
    

    【讨论】:

    • 我个人不同意SIMILAR TO。我在回答中写了更多关于此的内容。此外,值得一提的是,即使是正则表达式运算符~,也支持非常常见的左锚搜索模式。
    • @ErwinBrandstetter 啊,我忘了把它锚定在左边。会更正我的帖子。
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

      【讨论】:

      • 只是一个意见(因此是一个评论),但我不喜欢 ~~ 运算符,因为它是 Postgres 特定的并且失去了 SQL 可移植性而没有真正的收获。
      • @syrion:抱歉不清楚,我完全同意——我只是说~LIKE 做的事情不同。
      • @syrion 而且它并不比LIKE 更容易搜索,例如我必须在 Google 上输入 tilde tilde 才能到达这里
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-03
      • 2019-05-14
      • 1970-01-01
      • 1970-01-01
      • 2021-04-21
      • 2020-06-08
      • 2020-02-13
      相关资源
      最近更新 更多