Regex là viết tắt của Regular Expression (biểu thức chính quy), một phương pháp linh hoạt và mạnh mẽ để so sánh chuỗi sử dụng các khuôn mẫu (được gọi là pattern, hơi khó hiểu nhưng lát nữa đọc hết bạn sẽ hình dung được). Bài viết được xây dựng dựa theo MySQL Document, chạy trên MySQL 8.0.26.
Thông thường, khi ta muốn lấy dữ liệu từ MySQL chúng ta thường sử dụng lệnh SELECT
với điều kiện nào đó. Ví dụ:
SELECT * FROM student_table WHERE name = 'Hoang Thuy Linh';
Tuy nhiên nếu muốn tăng độ khó lên 1 chút như tìm tất cả những người tên Linh thì ta sử dụng LIKE
SELECT * FROM student_table WHERE name LIKE '%Linh%';
Tuy nhiên muốn tìm những người tên Linh mà địa chỉ nhà không có số nhà thì làm thế nào. Lúc này câu lệnh REGEX rất hữu dụng.
SELECT * FROM student_table
WHERE name LIKE '%Linh%' AND address NOT REGEXP '[0-9]';
Trong đó address là từ khóa để so sánh, ‘[0-9]’ là pattern đặc tả dữ liệu mà ta mong muốn so sánh.
Một số hàm biểu thức chính quy
chuỗi REGEXP pattern : So sánh chuỗi và pattern sau đó trả về kết quả 1 nếu khớp, ngược lại trả về 0
chuỗi NOT REGEXP pattern : Ngược lại với REGEXP
REGEXP_LIKE(chuỗi, pattern [,kiểu tùy chọn]) : Hàm này trả về kết quả là 1 nếu chuỗi khớp với pattern, nếu không trả về 0. Nếu chuỗi hoặc pattern là NULL nó sẽ trả về NULL
REGEXP_INSTR(chuỗi, pattern[, vị trí bắt đầu tìm kiếm, tần suất xuất hiện, giá trị trả về, kiểu tùy chọn]) : Hàm này trả về vị trí bắt đầu của chuỗi con trong chuỗi lớn khớp với pattern.
Cú pháp của các pattern
^: Khớp với phần bắt đầu 1 chuỗi
SELECT 'Linh' REGEXP '^Linh'; -- kết quả 1
SELECT 'Hello Linh' REGEXP '^Linh'; -- kết quả 0
$: Khớp với phần kết thúc 1 chuỗi
SELECT 'Linh hello' REGEXP 'Linh$'; -- kết quả 0
SELECT 'Hello Linh' REGEXP 'Linh$'; -- kết quả 1
. Dấu chấm này sẽ đại diện cho ký tự đơn bất kỳ
SELECT 'Linh hello' REGEXP 'Linh.'; -- kết quả 1, vì phía sau Linh có ký tự dấu cách
SELECT 'Hello Linh' REGEXP 'Linh.'; -- kết quả 0, vì phía sau Linh không còn ký tự nào
a*: Dấu hoa thị * thể hiện ký tự “a” xuất hiện từ 0 đến nhiều lần (chỉ duy nhất ký tự “a” thôi nhé)
SELECT 'Hello Linh nn' REGEXP 'Lin*h'; -- kết quả 1
SELECT 'Hello Lih' REGEXP 'Lin*h'; -- kết quả 1
SELECT 'Hello Linnnnh' REGEXP 'Lin*h'; -- kết quả 1
a+: Dấu + thể hiện chữ “a” xuất hiện từ 1 đến nhiều lần (chỉ duy nhất chữ “a” thôi nhé)
SELECT 'Hello Linh' REGEXP 'Lin+h'; -- kết quả 1
SELECT 'Hello Lih' REGEXP 'Lin+h'; -- kết quả 0
a?: Dấu ? thể hiện ký tự “a” xuất hiện 1 lần hoặc không xuất hiện
SELECT 'Hello Lnh' REGEXP 'Li?nh'; -- kết quả 1
SELECT 'Hello Linh' REGEXP 'Li?nh'; -- kết quả 1
SELECT 'Hello Liinh' REGEXP 'Li?nh'; -- kết quả 0
de|abc: Dấu | ở giữa thể hiện cho việc khớp chuỗi “de” hoặc chuỗi “abc”
SELECT 'Hello Linh' REGEXP 'Linh|Huong'; -- kết quả 1
SELECT 'Hello Duyen' REGEXP 'Linh|Huong'; -- kết quả 0
SELECT 'Hello Huong' REGEXP 'Linh|Huong'; -- kết quả 1
SELECT 'Huong' REGEXP '^(Linh|Huong)$'; -- kết quả 1
SELECT 'Linh' REGEXP '^(Linh|Huong)$'; -- kết quả 1
SELECT 'Huongx' REGEXP '^(Linh|Huong)$'; -- kết quả 0
(abc)* : thể hiện chuỗi “abc” xuất hiện từ 0 đến nhiều lần
SELECT 'Linh' REGEXP '^(Linh)*$'; -- kết quả 1
SELECT 'HelloLinh' REGEXP '^(Linh)*$'; -- kết quả 0
SELECT 'LinhLinh' REGEXP '^(Linh)*$'; -- kết quả 1
(abc){n} hoặc (abc){n,m} thể hiện sự xuất hiện của chuỗi “abc” xuất hiện chính xác n lần hoặc từ n đến m lần (n bắt buộc phải nhỏ hơn hoặc bằng m). Nếu theo như cách thể hiện này thì:
- a* có thể viết là a{0,}
- a+ có thể viết là a{1,}
- a? có thể viết là a{0,1}
SELECT 'Linh' REGEXP '(Linh){2}'; -- kết quả 0
SELECT 'Hello LinhLinh' REGEXP '(Linh){2}'; -- kết quả 1
SELECT 'Hello Linh Linh' REGEXP '(Linh){2}'; -- kết quả 0
Giải thích về pattern (Linh){2} tức là chuỗi “Linh” xuất hiện 2 lần liền kề nhau, do đó chuỗi “Hello Linh Linh” không hợp lệ do có dấu cách. Điều này các bạn cần lưu ý vì nhiều bạn hiểu rằng chữ “Linh” cứ xuất hiện 2 lần là sai nhé
[a-dX] hoặc [^a-dX] thể hiện sự xuất hiện của một trong bất kỳ ký tự nào trong cặp dấu ngoặc vuông a hoặc b hoặc c hoặc d hoặc X, nếu có dấu ^ thì ngược lại
SELECT 'aYbc' REGEXP '[a-dXYZ]'; -- Kết quả 1
SELECT 'aYbc' REGEXP '^[a-dXYZ]$'; -- Kết quả 0
SELECT 'aYbc' REGEXP '^[a-dXYZ]+$'; -- Kết quả 1
SELECT 'aYbc' REGEXP '^[^a-dXYZ]+$'; -- Kết quả 0
SELECT 'Linh' REGEXP '^[^a-dXYZ]+$'; -- Kết quả 1
SELECT 'Linha' REGEXP '^[^a-dXYZ]+$'; -- Kết quả 0
SELECT 'ab56' REGEXP '^[a-d1-9]+$'; -- Kết quả 1
[:character_class:] MySQL cung cấp 1 số class để khớp pattern với chuỗi ví dụ class số, class chữ, class ký tự đặc biệt. Các tên class cơ bản mình viết ở bên dưới nhé
SELECT 'abc56' REGEXP '^[[:alnum:]]+$'; -- Kết quả 1
SELECT 'abc56' REGEXP '^[[:alpha:]]+$'; -- Kết quả 0
SELECT ' ' REGEXP '^[[:blank:]]+$'; -- Kết quả 1
alnum | Các ký tự chữ và số |
alpha | Các ký tự chữ |
blank | Các ký tự khoảng trắng |
cntrl | Các ký tự điều khiển |
digit | Các ký tự số |
graph | Các ký tự đồ họa |
lower | Các ký tự chữ thường |
Các ký tự đồ họa và khoảng trắng | |
punct | Các ký tự chấm câu |
space | Các ký tự khoảng trắng, tab (\t), xuống dòng (\n) hoặc Carriage Return (\r) |
upper | Các ký tự chữ hoa |
xdigit | Các ký tự là chữ số thập lục phân (Hexadecimal digit) |
\ : Dấu \ được sử dụng trước các ký tự đặc biệt để khớp pattern với chuỗi
SELECT '1+2' REGEXP '1+2'; -- Kết quả 0
SELECT '1+2' REGEXP '1\+2'; -- Kết quả 0
SELECT '1+2' REGEXP '1\\+2'; -- Kết quả 1
Một số ví dụ về các pattern của REGEX
^[aeiou].* : Tìm tên của người dùng bắt đầu bằng nguyên âm
^0[^0][0-9]{8}$: Kiểm tra số điện thoại có 10 chữ số nhập vào là hợp lệ thỏa mãn các điều kiện sau: chữ số 0 ở đầu, chữ số tiếp theo khác 0, chỉ cho phép nhập 10 chữ số
^([0-9]+[a-zA-Z]+|[a-zA-Z]+[0-9]+)[0-9a-zA-Z]*$: Kiểm tra biển số xe nhập vào theo điều kiện chuỗi nhập vào bao gồm cả chữ và số