当我们不能完全确定需要查找的文本信息时,可以使用模式匹配进行模糊搜索。
DuckDB 提供了四种模式匹配方法:LIKE、SIMILAR TO、GLOB 以及正则表达式函数。
DuckDB 正则表达式函数不仅可以实现复杂的模式匹配,还可以执行字符串的提取、替换等操作,我们后续单独进行介绍。
LIKE
SQL 最常用的模式匹配方式就是采用 LIKE 运算符。假如我们想要知道姓“关”的员工有哪些,可以使用以下查询:
SELECTemp_nameFROMemployeeWHEREemp_nameLIKE'关%';┌──────────┐ │ emp_name │ │varchar│ ├──────────┤ │ 关羽 │ │ 关平 │ │ 关兴 │ └──────────┘其中,LIKE 关键字指定了一个字符串匹配模式,查找姓名以“关”字开头的员工。
LIKE 运算符支持以下两个通配符,可以用于指定匹配的模式:
- 百分号(%),表示匹配零个或者多个任意字符。
- 下划线(_),表示匹配一个任意字符。
以下是一些常用的模式和匹配的字符串:
- LIKE ‘en%’,匹配以“en”开始的字符串,例如“english”。
- LIKE ‘%en%’,匹配包含“en”的字符串,例如“length”。
- LIKE ‘%en’,匹配以“en”结束的字符串,例如“ten”。
- LIKE ‘Be_’,匹配以“Be”开头,再加上一个任意字符的字符串。例如“Bed”、“Bet”。
- LIKE ‘_e%’,匹配一个任意字符加上“e”开始的字符串,例如“he”、“year”。
由于百分号和下划线是 LIKE 运算符中的通配符,如果我们查找的模式中包含了“%”或者“_”,需要用到转义字符(escape character)。
转义字符可以将通配符当作普通字符使用。我们首先创建一个测试表:
CREATETABLEt_like(c1VARCHAR(200));INSERTINTOt_like(c1)VALUES('项目进度:25%已完成');INSERTINTOt_like(c1)VALUES('记录日期:2021年5月25日');t_like 只有一个字段 c1,数据类型为字符串,表中包含两条记录。
假如现在我们需要查找包含“25%”的数据,其中百分号是要查找的内容而不是任意多个字符,可以使用转义字符进行查找:
SELECTc1FROMt_likeWHEREc1LIKE'%25#%%'ESCAPE'#';┌─────────────────────┐ │ c1 │ │varchar│ ├─────────────────────┤ │ 项目进度:25%已完成 │ └─────────────────────┘ESACPE 关键字为 LIKE 运算符指定了一个 # 符号作为转义字符,因此查找模式中的第二个 % 代表了百分号,其他的 % 则是通配符。
使用 LIKE 运算符进行文本查找时,需要区分英文字母的大小写;如果不想区分大小写,可以使用 ILIKE 运算符。例如,以下语句使用大写字母查找员工的电子邮箱:
SELECTemailFROMemployeeWHEREemailLIKE'M%';┌─────────┐ │ email │ │varchar│ └─────────┘0rowsSELECTemailFROMemployeeWHEREemailILIKE'M%';┌──────────────────┐ │ email │ │varchar│ ├──────────────────┤ │ madai@shuguo.com│ │ mizhu@shuguo.com│ └──────────────────┘NOT LIKE 运算符可以执行与 LIKE 运算符相反的操作,也就是返回不匹配某个模式的文本。例如,以下语句查找 t_like 表中不包含“25%”的记录:
SELECTc1FROMt_likeWHEREc1NOTLIKE'%25#%%'ESCAPE'#';┌─────────────────────────┐ │ c1 │ │varchar│ ├─────────────────────────┤ │ 记录日期:2021年5月25日 │ └─────────────────────────┘DuckDB 还提供了以下 PostgreSQL 风格的运算符:~~ 等价于 LIKE,!~~ 等价于 NOT LIKE,~~* 等价于 ILIKE,!~~* 等价于 NOT ILIKE
SIMILAR TO
SIMILAR TO 结合了 LIKE 运算符和正则表达式的特性,提供了字符集、重复匹配、分组、选择等部分正则功能。
前文查找包含“25%”的示例可以使用 SIMILAR TO 实现:
SELECTc1FROMt_likeWHEREc1 SIMILARTO'.*25%.*';┌─────────────────────┐ │ c1 │ │varchar│ ├─────────────────────┤ │ 项目进度:25%已完成 │ └─────────────────────┘其中,.* 表示匹配零个或者多个任意字符(换行符除外)。
SIMILAR TO 也必须全字符串匹配,例如:
SELECT'abc'SIMILARTO'a';┌───────────────────────────────┐ │ regexp_full_match('abc','a')│ │boolean│ ├───────────────────────────────┤ │false│ └───────────────────────────────┘-- NOT SIMILAR TO执行相反匹配SELECT'abc'NOTSIMILARTO'abc';┌───────────────────────────────────────┐ │(NOTregexp_full_match('abc','abc'))│ │boolean│ ├───────────────────────────────────────┤ │false│ └───────────────────────────────────────┘另外,~ 运算符等价于 SIMILAR TO,!~ 运算符等价于 NOT SIMILAR TO。
GLOB
GLOB 来源于 Unix Shell 的文件名通配规则,DuckDB 支持使用GLOB 运算符作为一种模式匹配方法。例如:
SELECT'output.csv'GLOB'out*.csv';┌───────────────────────────────┐ │('output.csv'~~~'out*.csv')│ │boolean│ ├───────────────────────────────┤ │true│ └───────────────────────────────┘从查询结果可以看出,GLOB 运算符等价于三重波浪号(~~~)。
DuckDB 还提供了一个 glob 函数,可以用于搜索文件名。例如:
SELECT*FROMglob('*');┌─────────────────────────┐ │file│ │varchar│ ├─────────────────────────┤ │.\databases.csv │ │.\databases2.csv │ │.\duckdb-cheatsheet.pdf │ │.\duckdb-docs.pdf │ │.\duckdb-releases.csv │ │.\duckdb.exe │ │.\hr.duckdb │ │.\hr.duckdb.wal │ │.\output.csv │ │.\test.duckdb │ │.\ui │ └─────────────────────────┘11rows以上查询返回了当前工作目录下的所有文件。