我被指示“不要打扰LIKE
”并~
改为使用。有什么问题,有什么LIKE
不同~
?
在这种情况下是否~
有名称或人们是否说“使用波浪号运算符”?
~
是正则表达式运算符,并具有其中隐含的功能。您可以指定全范围的正则表达式通配符和量词;有关详细信息,请参阅文档。它肯定比 更强大LIKE
,并且应该在需要该功能时使用,但它们用于不同的目的。
没有任何问题,LIKE
而且,IMO,没有理由赞成~
它。而是相反。LIKE
是 SQL 标准的。也是SIMILAR TO
,但它没有得到广泛支持。PostgreSQL ~ operator
(或posix 正则表达式匹配运算符)不是 SQL 标准。
出于这个原因,我更喜欢LIKE
在表达能力足够的地方使用,并且我只~
在需要完整正则表达式的功能时使用。如果我需要移植数据库,那就少了一件伤害的事情。我倾向于使用SIMILAR TO
whenLIKE
不够强大,但在 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)
LIKE
,SIMILAR TO
并且是PostgreSQL~
中的基本模式匹配运算符。
如果可以,请使用LIKE
( ~~
),它是最快和最简单的。
如果不能,请使用正则表达式 ( ~
),它更强大。
从不用户. 这是毫无意义。见下文。SIMILAR TO
安装附加模块 pg_trgm添加高级索引选项和相似性运算符%
。
还有带有自己的基础设施和运营商(等等)的文本搜索。@@
这些运算符中的每一个都可以在不同程度上使用索引支持。它经常胜过其他选项的性能。但是在细节上还有很大的余地,即使有索引。
如果没有pg_trgm,则只有左锚定搜索模式的索引支持。如果您的数据库集群使用非 C 语言环境(典型情况)运行,您需要一个带有特殊运算符类的索引,例如text_pattern_ops
orvarchar_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_ops
或gin_trgm_ops
. 这些索引支持任何 LIKE
表达式,而不仅仅是左锚定。并且,引用手册:
从 PostgreSQL 9.3 开始,这些索引类型也支持正则表达式匹配的索引搜索。
细节:
SIMILAR TO
是一个非常奇怪的结构。PostgreSQL 只实现它,因为它是在 SQL 标准的早期版本中定义的。在内部,每个SIMILAR TO
表达式都用正则表达式重写。因此,对于任何给定的表达式,至少有一个正则表达式可以更快SIMILAR TO
地完成相同的工作。我从不使用.SIMILAR TO
进一步阅读:
运算符~~
等价于。LIKE
~
另一方面,将使用 POSIX 正则表达式进行匹配。
我只是做了一个快速简单的基准测试,以查看不涉及索引时两个运算符之间的性能差异:
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)
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
或in
或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 次或多次的字母次。
希望这个详细的分析有所帮助。
是的,它代表 POSIX 正则表达式。另一种选择是使用带有“SIMILAR TO”运算符的正则表达式的 SQL 标准方法,虽然它提供了更有限的一组功能,但可能更容易理解。我认为这是 dba 交换的一个很好的参考: https ://dba.stackexchange.com/questions/10694/pattern-matching-with-like-similar-to-or-regular-expressions-in-postgresql