字符串的模式匹配(pattern matching)是指給定一個模式,然後判斷某個字符串是否滿足該模式。常見的應用包括敏感詞的檢測、身份證校驗、IP 地址驗證等。
本文以驗證電子郵箱的合法性爲例,介紹如何在 SQL 中實現字符串的模式匹配,涉及的數據庫包括 MySQL、Oracle、SQL Server、PostgreSQL 以及 SQLite。下表列出了這些數據庫對於模式匹配的支持情況:
模式匹配 | MySQL | Oracle | SQL Server | PostgreSQL | SQLite |
---|---|---|---|---|---|
LIKE 運算符 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
正則表達式 | REGEXP_LIKE() | REGEXP_LIKE() | ~ |
接下來我們詳細討論不同數據庫的具體實現細節。
準備工作
我們首先創建一個測試表,並且生成一些測試數據:
CREATE TABLE test (
email VARCHAR(50)
);
INSERT INTO test VALUES ('[email protected]');
INSERT INTO test VALUES ('test@qq');
INSERT INTO test VALUES ('[email protected]');
INSERT INTO test VALUES ('[email protected]');
INSERT INTO test VALUES ('me.qq.com');
INSERT INTO test VALUES ('[email protected]');
對於 Web 開發中常見的郵箱地址驗證問題,我們首先需要定義一個合法電子郵箱的規則。電子郵箱一般格式爲:用戶名@域名;以下是一個簡單的規則:
- 以字母或者數字開頭,後面跟一個或者多個字母、數組或特殊字符( . _ - );
- 然後是一個 @ 字符;
- 接着是一個或者多個字母、數組或特殊字符( . - );
- 最後是域名,即 . 以及 2 到 4 個字母。
對於以上規則,使用正則表達式可以描述如下:
^[a-zA-Z0-9]+[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$
簡單說明一下,^ 表示匹配字符串的開頭;[a-zA-Z0-9] 匹配大小寫字母或數字;+ 表示匹配前面的內容一次或多次;. 匹配任何一個字符,\. 匹配點號自身;{2,4} 匹配前面的內容 2 次到 4次;$ 表示字符串的結束。
📝關於正則表達式的詳細介紹,可以參考 GitHub 上的正則表達式教程。
LIKE 運算符
SQL 標準定義了 LIKE
運算符,可以用於簡單的模式匹配。其中有兩個特殊的通配符:
- 下劃線(_)匹配一個任意字符;
- 百分號(%)匹配零個或者多個任意字符。
以下語句用於查找以 test 開頭的字符串:
select email
from test
where email like 'test%';
-- MySQL、SQL Server 和 SQLite
email |
------------------|
TEST@qq.com |
test@qq |
test+email@sina.cn|
-- Oracle 和 PostgreSQL
EMAIL |
------------------|
test@qq |
test+email@sina.cn|
從查詢結果可以看出,MySQL、SQL Server 和 SQLite 中的LIKE
運算符默認不區分大小寫;Oracle 和 PostgreSQL 中的LIKE
運算符默認區分大小寫。
以下示例返回了由 4 個字符加上 @ 符號開始的字符串:
select email
from test
where email like '____@%';
email |
-----------|
TEST@qq.com|
test@qq |
.123@qq.com|
另外,NOT LIKE
運算符可以進行反向匹配,也就是查找不匹配某個模式的字符串。
轉義字符
由於下劃線和百分號被看作通配符,我們無法判斷某個字符串中是否包含這兩個字符。此時需要用到轉義字符,它的作用就是將隨後的字符看作普通字符,例如:
'60%' like '60!%' escape '!' -- 匹配
'60%' like '60\%' -- 匹配
'60@' like '60!%' escape '!' -- 不匹配
其中,escape 用於指定轉義字符,默認情況下爲反斜槓(\)。
數據庫擴展
除了 SQL 標準中定義的行爲之外,許多數據庫對LIKE運算符進行了擴展:
- MySQL 和 SQLite 允許對數字類型進行模式匹配:
10 LIKE '1%'
; - Oracle 提供了
LIKEC
、LIKE2
、LIKE4
用於不同字符集的字符匹配; - SQL Server 支持使用 [ ] 匹配某個範圍內的字符,[^ ] 匹配某個範圍之外的字符;
- PostgreSQL 提供了不區分大小寫的
ILIKE
運算符; - SQLite 提供了區分大小寫的編譯選項
PRAGMA case_sensitive_like = true;
,同時還提供了基於 Unix 文件路徑匹配的 GLOB 運算符。
我們主要來看一下 SQL Server 中的範圍匹配,例如:
-- SQL Server
select email
from test
where email like '[0-9]%';
email |
----------------|
123.test@sql.org|
其中,[0-9] 表示任意數字;所以上面的查詢返回了以數字開頭的字符串。同理,'[^0-9]%'
匹配不以數字開頭的字符串,%[ace]
匹配以 a、c 或者 e 結束的字符串。
LIKE
運算符適合簡單的模式匹配。當我們需要匹配複雜的模式時,例如合法的電子郵箱,無法使用LIKE
運算符實現。此時,需要使用更強大的正則表達式。
MySQL 正則表達式
MySQL 提供了 REGEXP_LIKE 函數,用於實現正則表達式匹配:
REGEXP_LIKE(expr, pat[, match_type])
如果表達式 expr 匹配模式 pat,該函數返回 1,否則返回 0;如果 expr 或者 pat 爲 NULL,該函數返回 NULL。可選的參數 match_type 可以指定多個額外的匹配選項:
- c:區分大小寫;
- i:不區分大小寫;
- m:多行模式,^ 和 $ 能夠匹配字符串中的行終止符。默認情況下它們只匹配 expr 的開頭和結尾;
- n:字符 . 能夠匹配行終止符。默認情況下 . 遇到行尾將會匹配;
- u:只匹配 Unix 行終止符,只有換行符(\n)能夠匹配 .、^ 以及 $。
我們使用正則表達式實現上文中的電子郵箱校驗:
select email
from test
where regexp_like(email, '^[a-z0-9]+[a-z0-9._-]+@[a-z0-9.-]+\\.[a-z]{2,4}$');
email |
----------------|
TEST@qq.com |
123.test@sql.org|
查詢結果顯式只有 2 個電子郵箱是合法的。注意,模式中的反斜槓需要寫雙份,因爲 MySQL 使用 C 語言風格的轉義語法。
另外,REGEXP 和 RLIKE 運算符是 REGEXP_LIKE 函數的同義詞:
expr REGEXP pat
expr RLIKE pat
Oracle 正則表達式
Oracle 提供了與 MySQL 類似的 REGEXP_LIKE 函數,用於執行正則表達式匹配:
REGEXP_LIKE(source_char, pattern[, match_param ])
Oracle 中的 REGEXP_LIKE 只能作爲條件使用;如果字符串 source_char 匹配模式 pat 就返回結果;否則不返回結果。可選的參數 match_param 可以指定多個額外的匹配選項:
- c 表示區分大小寫和重音;
- i 表示不區分大小寫和重音;
- n 表示符號 . 能夠匹配換行符(\n)。默認情況下不匹配換行符;
- m 表示多行模式,^ 和 $ 能夠匹配字符串中的任何行的開始和結尾,而不僅僅是整個字符串的開頭和結尾;
- x 表示忽略模式中的空白字符,默認情況下空白字符匹配它們自身。
如果省略 match_param 參數,是否區分大小寫和重音取決於數據的字符集排序規則;同時符號 . 不會匹配換行符,並且字符串 source_char 被看做單行字符串。
對於上文中的電子郵箱校驗,Oracle 中的實現如下:
select email
from test
where regexp_like(email, '^[a-z0-9]+[a-z0-9._-]+@[a-z0-9.-]+\.[a-z]{2,4}$', 'i');
email |
----------------|
TEST@qq.com |
123.test@sql.org|
選項 i 表示不區分大小寫。
SQL Server 正則表達式
SQL Server 目前沒有提供類似其他數據庫的正則表達式函數或者運算符,可以通過 CLR 編寫自定義函數實現。
PostgreSQL 正則表達式
PostgreSQL 提供了一組相關的正則表達式匹配運算符:
expr ~ pattern
,正則表達式匹配,區分大小寫;expr ~* pattern
,正則表達式匹配,不區分大小寫;expr !~ pattern
,不匹配正則表達式,區分大小寫;expr !~* pattern
,不匹配正則表達式,區分大小寫;
對於電子郵箱的合法性驗證,可以使用以下查詢語句:
select email
from test
where email ~* '^[a-z0-9]+[a-z0-9._-]+@[a-z0-9.-]+\.[a-z]{2,4}$';
email |
----------------|
TEST@qq.com |
123.test@sql.org|
上例中使用了不區分大小寫的匹配運算符。
另外,PostgreSQL 還提供了一個運算符SIMILAR TO
,用於執行一種特殊的 SQL 正則表達式匹配。不過,SQL 標準已經刪除了這個運算符,不推薦使用。
SQLite 正則表達式
SQLite 默認沒有提供了正則表達式匹配的運算符,但是預定義了 X REGEXP Y 運算符接口,它實際上是調用了 regexp(Y,X) 用戶函數。因此,我們可以通過創建一個函數 regexp(Y,X),例如安裝並加載 sqlite3-pcre 模塊,就可以獲得基於 Perl 的正則表達式。
參考文檔
以下是各種數據庫關於字符串模式匹配的官方文檔,使用時可以參考:
定期更新數據庫領域相關文章,歡迎關注❤️、評論📝、點贊👍!