好的,我们开始今天的讲座,主题是利用 REGEXP 与 RLIKE 进行正则表达式匹配。 在 MySQL 中,REGEXP 和 RLIKE 运算符都用于执行正则表达式匹配。它们本质上是同义词,可以互换使用。本讲座将深入探讨如何有效地利用这两个运算符,包括正则表达式的语法、常用模式、以及在实际查询中的应用。
一、正则表达式基础
正则表达式是一种强大的文本模式匹配工具,它允许我们使用特定的语法来描述要查找的字符模式。掌握正则表达式的语法是使用 REGEXP 和 RLIKE 的前提。
-
字符匹配
.(点号): 匹配任意单个字符,除了换行符 (在某些配置下可能匹配换行符)。[abc]: 匹配方括号中的任意一个字符(在本例中是 a、b 或 c)。[^abc]: 匹配除了方括号中的字符之外的任意字符。[a-z]: 匹配从 a 到 z 的任意小写字母。[A-Z]: 匹配从 A 到 Z 的任意大写字母。[0-9]: 匹配从 0 到 9 的任意数字。d: 匹配任意数字,等价于[0-9]。D: 匹配任意非数字字符,等价于[^0-9]。w: 匹配任意字母数字字符或下划线,等价于[a-zA-Z0-9_]。W: 匹配任意非字母数字字符或下划线,等价于[^a-zA-Z0-9_]。s: 匹配任意空白字符(空格、制表符、换行符等)。S: 匹配任意非空白字符。
-
量词
*: 匹配前面的字符零次或多次。+: 匹配前面的字符一次或多次。?: 匹配前面的字符零次或一次。{n}: 匹配前面的字符恰好 n 次。{n,}: 匹配前面的字符至少 n 次。{n,m}: 匹配前面的字符至少 n 次,但不超过 m 次。
-
定位符
^: 匹配字符串的开头。$: 匹配字符串的结尾。b: 匹配单词边界。B: 匹配非单词边界。
-
转义字符
: 用于转义特殊字符,例如.匹配点号字符,\匹配反斜杠字符。
-
分组与捕获
( ): 将括号中的内容作为一个分组,可以用于量词的应用和后续的引用。|: 或操作符,匹配左右两侧的任意一个模式。
二、REGEXP 和 RLIKE 的使用
REGEXP 和 RLIKE 运算符用于 WHERE 子句中,用于筛选满足特定正则表达式模式的记录。 基本语法如下:
SELECT column1, column2, ...
FROM table_name
WHERE column_name REGEXP 'pattern';
SELECT column1, column2, ...
FROM table_name
WHERE column_name RLIKE 'pattern';
其中:
column_name: 要匹配的列名。pattern: 正则表达式模式。
三、常用正则表达式模式示例
下面是一些常见的正则表达式模式及其在 REGEXP 和 RLIKE 中的应用示例。 假设我们有一个名为 products 的表,包含 id (INT), name (VARCHAR), 和 description (TEXT) 列。
-
匹配以特定字符串开头的记录
-- 查找名称以 "A" 开头的产品 SELECT * FROM products WHERE name REGEXP '^A'; SELECT * FROM products WHERE name RLIKE '^A'; -
匹配以特定字符串结尾的记录
-- 查找名称以 "z" 结尾的产品 SELECT * FROM products WHERE name REGEXP 'z$'; SELECT * FROM products WHERE name RLIKE 'z$'; -
匹配包含特定字符串的记录
-- 查找名称包含 "apple" 的产品 SELECT * FROM products WHERE name REGEXP 'apple'; SELECT * FROM products WHERE name RLIKE 'apple'; -
匹配包含数字的记录
-- 查找描述包含数字的产品 SELECT * FROM products WHERE description REGEXP '[0-9]'; SELECT * FROM products WHERE description RLIKE '[0-9]'; -- 或者使用 d SELECT * FROM products WHERE description REGEXP '\d'; SELECT * FROM products WHERE description RLIKE '\d'; -
匹配包含特定字符集中的字符的记录
-- 查找名称包含 "a"、"b" 或 "c" 的产品 SELECT * FROM products WHERE name REGEXP '[abc]'; SELECT * FROM products WHERE name RLIKE '[abc]'; -
匹配不包含特定字符集中的字符的记录
-- 查找名称不包含 "a"、"b" 或 "c" 的产品 SELECT * FROM products WHERE name REGEXP '[^abc]'; SELECT * FROM products WHERE name RLIKE '[^abc]'; -
匹配特定长度的字符串
-- 查找名称长度为 5 个字符的产品 SELECT * FROM products WHERE name REGEXP '^.....$'; -- 使用五个点号 SELECT * FROM products WHERE name RLIKE '^.....$'; -- 或者使用 {n} 量词 SELECT * FROM products WHERE name REGEXP '^.{5}$'; SELECT * FROM products WHERE name RLIKE '^.{5}$'; -
匹配包含重复字符的记录
-- 查找名称中包含连续两个相同字符的产品 SELECT * FROM products WHERE name REGEXP '(.)\1'; SELECT * FROM products WHERE name RLIKE '(.)\1';解释:
(.)匹配任意字符并将其捕获到第一个分组中。\1引用第一个分组中捕获的内容。 -
匹配符合特定格式的字符串 (例如,电子邮件地址)
-- 假设 products 表有一个 email 列 SELECT * FROM products WHERE email REGEXP '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'; SELECT * FROM products WHERE email RLIKE '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$';解释:
^[a-zA-Z0-9._%+-]+: 匹配电子邮件地址的用户名部分(以字母、数字、点号、下划线、百分号、加号或减号开头,且至少包含一个字符)。@: 匹配 @ 符号。[a-zA-Z0-9.-]+: 匹配域名部分(以字母、数字、点号或减号开头,且至少包含一个字符)。\.: 匹配点号(需要转义)。[a-zA-Z]{2,}$: 匹配顶级域名(至少包含两个字母)。
-
使用
|(或) 操作符-- 查找名称包含 "apple" 或 "banana" 的产品 SELECT * FROM products WHERE name REGEXP 'apple|banana'; SELECT * FROM products WHERE name RLIKE 'apple|banana';
四、注意事项
-
性能
正则表达式匹配可能比简单的字符串比较更耗费资源,尤其是在大型数据集上。因此,应谨慎使用,并尽可能优化正则表达式模式。 考虑使用索引来提高包含
REGEXP或RLIKE的查询的性能。 -
字符集
确保数据库的字符集与正则表达式模式的字符集一致,以避免意外的匹配错误。
-
转义特殊字符
在正则表达式中,某些字符具有特殊含义(例如
.、*、+、?、^、$、[]、()、{}、|、)。如果要在模式中匹配这些字符本身,则需要使用反斜杠进行转义。 由于 SQL 中反斜杠本身也需要转义,因此在正则表达式中匹配反斜杠需要使用\\。 -
大小写敏感性
默认情况下,
REGEXP和RLIKE运算符执行大小写不敏感的匹配。 要执行大小写敏感的匹配,可以使用BINARY关键字。-- 大小写敏感的匹配 SELECT * FROM products WHERE BINARY name REGEXP 'Apple'; SELECT * FROM products WHERE BINARY name RLIKE 'Apple'; -
NULL 值处理
REGEXP和RLIKE运算符在应用于NULL值时,结果为NULL。 如果需要处理NULL值,可以使用COALESCE函数或IFNULL函数。-- 查找 email 列不为 NULL 且符合电子邮件地址格式的产品 SELECT * FROM products WHERE email IS NOT NULL AND email REGEXP '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'; -- 或者,如果允许 email 列为 NULL,可以使用 COALESCE 处理 SELECT * FROM products WHERE COALESCE(email, '') REGEXP '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$';
五、高级技巧
-
使用分组和反向引用进行更复杂的匹配
如前所述,使用
( )进行分组,并使用\1、\2等进行反向引用,可以实现更复杂的匹配逻辑。-- 查找名称中包含两个相同单词的产品 (例如,"hello hello world") SELECT * FROM products WHERE name REGEXP '(\w+)\s+\1'; SELECT * FROM products WHERE name RLIKE '(\w+)\s+\1';解释:
(\w+): 匹配一个或多个字母数字字符或下划线,并将其捕获到第一个分组中。\s+: 匹配一个或多个空白字符。\1: 引用第一个分组中捕获的内容。
-
结合其他 SQL 函数使用
可以将
REGEXP和RLIKE运算符与其他 SQL 函数结合使用,以实现更强大的数据处理功能。 例如,可以结合CASE语句进行条件判断,或者结合REPLACE函数进行字符串替换。-- 使用 CASE 语句根据名称是否包含 "apple" 来设置一个标志 SELECT name, CASE WHEN name REGEXP 'apple' THEN 'Contains Apple' ELSE 'Does Not Contain Apple' END AS apple_flag FROM products; -
使用预编译正则表达式 (适用于某些编程语言,但 MySQL 本身不支持)
在某些编程语言中(例如 Java、Python),可以将正则表达式预编译成一个对象,以便在多次匹配时提高性能。 但是,MySQL 本身不支持预编译正则表达式。 每次使用
REGEXP或RLIKE运算符时,MySQL 都会重新编译正则表达式。 如果需要进行大量的正则表达式匹配,可以考虑将数据导出到其他支持预编译正则表达式的编程环境中进行处理。
六、一些更复杂的例子
假设我们现在 products 表中有一个 url 字段, 我们需要校验这个 url 字段的合法性
SELECT * FROM products WHERE url REGEXP '^(http(s)?://.)?(www.)?[-a-zA-Z0-9@:%._+~#=]{2,256}.[a-z]{2,6}b([-a-zA-Z0-9@:%_+.~#?&//=]*)$';
SELECT * FROM products WHERE url RLIKE '^(http(s)?://.)?(www.)?[-a-zA-Z0-9@:%._+~#=]{2,256}.[a-z]{2,6}b([-a-zA-Z0-9@:%_+.~#?&//=]*)$';
这个正则表达式的解释如下:
^: 匹配字符串的开始。(http(s)?://.)?: 可选的http://或https://部分。http: 匹配 "http"。(s)?: 可选的 "s"。://: 匹配 "://"。.: 匹配任意字符。?: 表示前面的模式是可选的。
(www.)?: 可选的 "www." 部分。www: 匹配 "www"。.: 匹配 "." (需要转义)。?: 表示前面的模式是可选的。
[-a-zA-Z0-9@:%._+~#=]{2,256}: 匹配域名部分。[-a-zA-Z0-9@:%._+~#=]: 允许的字符集。{2,256}: 长度在 2 到 256 之间。
.[a-z]{2,6}: 匹配顶级域名部分。.: 匹配 "." (需要转义)。[a-z]{2,6}: 匹配 2 到 6 个小写字母。
b: 匹配单词边界。([-a-zA-Z0-9@:%_+.~#?&//=]*): 匹配可选的路径和查询参数部分。[-a-zA-Z0-9@:%_+.~#?&//=]: 允许的字符集。*: 表示前面的模式可以出现零次或多次。
$: 匹配字符串的结尾。
这个表达式已经比较完善,可以满足大部分 URL 的校验需求。
七、表格总结常用元字符
| 元字符 | 描述 | 示例 |
|---|---|---|
| . | 匹配任意单个字符,除了换行符 (在某些配置下可能匹配换行符) | a.c 匹配 "abc", "adc", "aec" 等 |
| [] | 匹配方括号中的任意一个字符 | [abc] 匹配 "a", "b", 或 "c" |
| [^] | 匹配除了方括号中的字符之外的任意字符 | [^abc] 匹配除了 "a", "b", 或 "c" 之外的任意字符 |
| d | 匹配任意数字,等价于 [0-9] |
dd 匹配 "12", "34", "56" 等 |
| D | 匹配任意非数字字符,等价于 [^0-9] |
DD 匹配 "ab", "cd", "ef" 等 |
| w | 匹配任意字母数字字符或下划线,等价于 [a-zA-Z0-9_] |
ww 匹配 "a1", "b2", "c3" 等 |
| W | 匹配任意非字母数字字符或下划线,等价于 [^a-zA-Z0-9_] |
WW 匹配 "!!", "$$", "%%" 等 |
| s | 匹配任意空白字符(空格、制表符、换行符等) | asb 匹配 "a b", "atb" 等 |
| S | 匹配任意非空白字符 | aSb 匹配 "aab", "acb" 等 |
| * | 匹配前面的字符零次或多次 | a*b 匹配 "b", "ab", "aab", "aaab" 等 |
| + | 匹配前面的字符一次或多次 | a+b 匹配 "ab", "aab", "aaab" 等,但不匹配 "b" |
| ? | 匹配前面的字符零次或一次 | a?b 匹配 "b" 或 "ab" |
| {n} | 匹配前面的字符恰好 n 次 | a{3}b 匹配 "aaab" |
| {n,} | 匹配前面的字符至少 n 次 | a{2,}b 匹配 "aab", "aaab", "aaaab" 等 |
| {n,m} | 匹配前面的字符至少 n 次,但不超过 m 次 | a{2,4}b 匹配 "aab", "aaab", "aaaab",但不匹配 "ab" 或 "aaaaab" |
| ^ | 匹配字符串的开头 | ^abc 匹配以 "abc" 开头的字符串 |
| $ | 匹配字符串的结尾 | abc$ 匹配以 "abc" 结尾的字符串 |
| b | 匹配单词边界 | babcb 匹配独立的单词 "abc" |
| B | 匹配非单词边界 | BabcB 匹配包含 "abc" 但不是独立单词的部分,例如 "xabcx" |
| ( ) | 将括号中的内容作为一个分组,可以用于量词的应用和后续的引用。 | (abc)+ 匹配 "abc", "abcabc", "abcabcabc" 等, (.)\1匹配连续两个相同字符 |
| | | 或操作符,匹配左右两侧的任意一个模式。 | a|b 匹配 "a" 或 "b" |
八、总结与回顾
REGEXP 和 RLIKE 是 MySQL 中强大的正则表达式匹配工具,掌握其语法和应用技巧对于进行复杂的数据筛选和处理至关重要。 记住要关注性能,转义特殊字符,并结合其他 SQL 函数来实现更高级的功能。 熟练运用这些技巧,可以极大地提高数据处理的效率和灵活性。