我被指示“不要打扰LIKE
”,而要使用~
。什么是错的LIKE
又是怎样的~
不同?
~
在这种情况下是否有名称,或者人们说“使用波浪号运算符”?
Answers:
~
是正则表达式运算符,并具有其隐含的功能。您可以指定所有正则表达式通配符和量词;有关详细信息,请参见文档。它肯定比强大LIKE
,并且在需要此功能时应使用,但它们具有不同的用途。
LIKE
,通常最好使用正则表达式。通过适当的索引,它更简单,更快,更容易使用。正则表达式功能更强大,但速度较慢且非标准。
should be used in preference to the LIKE operator when appropriate
是错的。当您有选择时,请使用LIKE。如果您别无选择,那么您也别无选择。
LIKE
IMO没有错,也没有理由~
对此表示赞同。相反。LIKE
是SQL标准的。也是SIMILAR TO
,但并未得到广泛支持。PostgreSQL的~ operator
(或posix正则表达式匹配运算符)不是SQL标准。
因此,我更喜欢LIKE
在表达力足够大的地方使用,并且仅~
在需要完整正则表达式的功能时才使用。如果我需要移植数据库,那将是一件痛苦的事。我倾向于使用SIMILAR TO
whenLIKE
不够强大的功能,但是在Erwin发表评论之后,我想我会停止这样做,而~
在LIKE
不工作时使用。
此外,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时,它也要使用索引,即使它实际上更昂贵(由于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
。我在回答中写了更多有关此的内容。另外,值得一提的是,即使对于正则表达式运算符,也支持非常常见的左锚搜索模式~
。
LIKE
,SIMILAR TO
并且~
是基本的PostgreSQL中模式匹配运营商。
如果可以,请使用LIKE
(~~
),这是最快,最简单的。
如果不能,请使用正则表达式(~
),功能更强大。
永远不要用户。没有用。见下文。SIMILAR TO
安装附加模块pg_trgm将添加高级索引选项和相似性运算符%
。文本搜索
还具有其自身的基础设施和运营商(以及其他)。@@
这些操作员中的每一个都可以使用索引支持-程度不同。它经常胜过其他选择的性能。但是即使在使用索引的情况下,细节也有很多余地。
没有pg_trgm,只有索引支持左锚定搜索模式。如果您的数据库集群使用非C语言环境运行(典型情况),则需要一个带有特殊运算符类的索引,例如text_pattern_ops
或varchar_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
随着pg_trgm安装,杜松子酒或GiST索引可能与运营商类gist_trgm_ops
或gin_trgm_ops
。这些索引支持任何 LIKE
表达式,而不仅仅是锚定。并且,引用手册:
从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)不支持该标准。
该~~
操作相当于LIKE
。 ~
另一方面,将使用POSIX正则表达式进行匹配。
~~
运算符,因为它是Postgres特有的,并且失去了SQL可移植性而没有真正的收获。
LIKE
,例如,我不得不tilde tilde
在Google上键入才能到达这里
我只是做了一个快速而简单的基准测试,以研究不涉及索引的情况下两个运算符之间的性能差异:
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)
就像只匹配字符串的一部分,无论是在开头还是在末尾或中间。倾斜(〜)与正则表达式匹配
为了进一步解释这一点,让我们创建一个表并插入一些值
# 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
orin
或org
etc
and我们可以使用正则表达式创建模式。
现在让我们尝试使用正则表达式获取结果
# 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-like-to-or-regular-expressions-in-postgresql