【SQL 模式匹配】如何驗證電子郵箱的合法性?

puzzle

字符串的模式匹配(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 提供了LIKECLIKE2LIKE4用於不同字符集的字符匹配;
  • 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 的正則表達式。

參考文檔

以下是各種數據庫關於字符串模式匹配的官方文檔,使用時可以參考:

定期更新數據庫領域相關文章,歡迎關注❤️、評論📝、點贊👍!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章