Giải thích MySQL FIND_IN_SET: Cách tìm kiếm giá trị phân tách bằng dấu phẩy một cách chính xác

目次

1. Giới thiệu

Thách thức khi tìm kiếm dữ liệu phân tách bằng dấu phẩy trong MySQL

Khi làm việc với cơ sở dữ liệu, bạn có thể gặp các trường hợp mà nhiều giá trị được lưu trong một cột duy nhất và được ngăn cách bằng dấu phẩy. Ví dụ, một cột có thể chứa chuỗi như "1,3,5", và bạn muốn trích xuất chỉ những bản ghi có chứa giá trị “3”.

Trong những trường hợp như vậy, việc sử dụng toán tử chuẩn = hoặc mệnh đề IN thường không cho kết quả như mong đợi. Điều này là vì một chuỗi phân tách bằng dấu phẩy được coi là một giá trị chuỗi duy nhất, nghĩa là các phép so sánh được thực hiện trên toàn bộ chuỗi thay vì trên từng phần tử riêng lẻ bên trong nó.

Hàm FIND_IN_SET là gì?

Trong các tình huống như thế này, hàm MySQL FIND_IN_SET trở nên rất hữu ích.
Hàm này cho phép bạn dễ dàng xác định xem một giá trị cụ thể có tồn tại trong một chuỗi phân tách bằng dấu phẩy hay không.

Ví dụ, hãy xem xét câu lệnh SQL sau đây:

SELECT * FROM users WHERE FIND_IN_SET('3', favorite_ids);

Trong truy vấn này, bạn có thể trích xuất các bản ghi mà chuỗi phân tách bằng dấu phẩy trong cột favorite_ids (ví dụ, "1,2,3,4") chứa giá trị “3”.

Mục đích của Bài viết và Đối tượng Độc giả

Bài viết này giải thích cách sử dụng hàm FIND_IN_SET từ những kiến thức cơ bản một cách rõ ràng và có cấu trúc. Từ cú pháp cơ bản đến các ví dụ thực tế, so sánh với các phương pháp tìm kiếm khác, các lưu ý quan trọng và câu hỏi thường gặp, hướng dẫn này cung cấp kiến thức thực tiễn cho việc phát triển thực tế.

Bài viết này hướng tới:

  • Các kỹ sư web và nhà phát triển backend thường xuyên sử dụng MySQL
  • Các nhà phát triển phải làm việc với các hệ thống hiện có lưu trữ dữ liệu phân tách bằng dấu phẩy
  • Người mới học SQL gặp khó khăn với việc khớp một phần và tìm kiếm dựa trên giá trị

2. Cú pháp Cơ bản và Hành vi của Hàm FIND_IN_SET

Cú pháp của FIND_IN_SET

FIND_IN_SET là một hàm MySQL dùng để xác định xem một giá trị cụ thể có tồn tại trong một chuỗi phân tách bằng dấu phẩy hay không. Cú pháp cơ bản như sau:

FIND_IN_SET(search_value, comma_separated_string)

Ví dụ:

SELECT FIND_IN_SET('3', '1,2,3,4'); -- Result: 3

Trong ví dụ này, vì “3” xuất hiện ở vị trí thứ ba, hàm sẽ trả về giá trị số 3.

Quy tắc Giá trị Trả về

Hàm FIND_IN_SET hoạt động theo các quy tắc sau:

ConditionResult
The search value exists in the listIts position in the list (starting from 1)
The search value does not exist0
Either argument is NULLNULL

Ví dụ (Trả về Vị trí)

SELECT FIND_IN_SET('b', 'a,b,c'); -- Result: 2

Ví dụ (Không tìm thấy Giá trị)

SELECT FIND_IN_SET('d', 'a,b,c'); -- Result: 0

Ví dụ (Bao gồm NULL)

SELECT FIND_IN_SET(NULL, 'a,b,c'); -- Result: NULL

Ví dụ Sử dụng trong Mệnh đề WHERE

Hàm này thường được sử dụng nhất để lọc dữ liệu trong một mệnh đề WHERE.

SELECT * FROM users WHERE FIND_IN_SET('admin', roles);

Trong ví dụ này, chỉ những hàng mà cột roles chứa chuỗi “admin” mới được trả về. Nếu cột chứa giá trị như "user,editor,admin", nó sẽ khớp.

Lưu ý Quan trọng về Số và Chuỗi

FIND_IN_SET thực hiện các phép so sánh dưới dạng chuỗi, nghĩa là nó hoạt động như sau:

SELECT FIND_IN_SET(3, '1,2,3,4');     -- Result: 3
SELECT FIND_IN_SET('3', '1,2,3,4');   -- Result: 3

Mặc dù nó hoạt động với cả giá trị số và chuỗi, việc không rõ ràng về kiểu dữ liệu có thể dẫn đến hành vi không mong muốn. Do đó, thực hành tốt nhất là xử lý các giá trị một cách rõ ràng dưới dạng chuỗi bất cứ khi nào có thể.

3. Các Ví dụ Thực tế

Tìm kiếm trong Cột Lưu trữ Chuỗi Phân tách Bằng Dấu phẩy

Trong các hệ thống thực tế, bạn có thể gặp các trường hợp mà nhiều giá trị (như ID hoặc quyền) được lưu trong một cột duy nhất dưới dạng chuỗi phân tách bằng dấu phẩy. Ví dụ, hãy xem bảng users sau đây.

idnamefavorite_ids
1Taro1,3,5
2Hanako2,4,6
3Jiro3,4,5

Khi bạn muốn “lấy các người dùng có chứa 3”, hàm FIND_IN_SET cực kỳ tiện lợi.

SELECT * FROM users WHERE FIND_IN_SET('3', favorite_ids);

Chạy câu lệnh SQL này sẽ trả về các bản ghi của “Taro” và “Jiro”.

Hoạt Động Tốt Ngay Cả Khi Các Giá Trị Trông Giống Số

Ngay cả khi favorite_ids dường như chứa các số, FIND_IN_SET thực hiện so sánh dựa trên chuỗi, vì vậy an toàn nhất là truyền đối số dưới dạng chuỗi có dấu ngoặc kép.

-- OK
SELECT * FROM users WHERE FIND_IN_SET('5', favorite_ids);

-- Works, but strictly speaking not recommended
SELECT * FROM users WHERE FIND_IN_SET(5, favorite_ids);

Để giữ cho các truy vấn dễ đọc và hành vi có thể dự đoán, khuyến nghị chỉ định rõ ràng giá trị dưới dạng chuỗi.

Tìm Kiếm Động (Chỗ Đặt Và Biến)

Khi tạo SQL động từ một ứng dụng web, việc sử dụng biến hoặc tham số ràng buộc là phổ biến.

Nếu bạn sử dụng biến MySQL, nó sẽ trông như thế này:

SET @target_id = '3';
SELECT * FROM users WHERE FIND_IN_SET(@target_id, favorite_ids);

Khi ràng buộc từ lớp ứng dụng (như PHP, Python hoặc Node.js), bạn có thể xử lý tương tự bằng cách sử dụng chỗ đặt.

Cách Xử Lý Tìm Kiếm Nhiều Giá Trị

Thật không may, FIND_IN_SET chỉ có thể tìm kiếm một giá trị tại một thời điểm.
Nếu bạn muốn lấy các bản ghi chứa “3 hoặc 4,” bạn phải viết nó nhiều lần bằng cách sử dụng OR.

SELECT * FROM users
WHERE FIND_IN_SET('3', favorite_ids) OR FIND_IN_SET('4', favorite_ids);

Nếu các điều kiện trở nên phức tạp hơn, bạn nên xây dựng SQL động trong ứng dụng của mình hoặc xem xét di chuyển sang cấu trúc bảng chuẩn hóa.

4. So Sánh FIND_IN_SET Với Các Phương Pháp Tìm Kiếm Khác

Các Lựa Chọn Thay Thế Phổ Biến: IN Và LIKE

Trong MySQL, ngoài FIND_IN_SET, bạn cũng có thể thấy mệnh đề IN hoặc mệnh đề LIKE được sử dụng để kiểm tra xem một giá trị có được bao gồm hay không. Tuy nhiên, mỗi phương pháp hoạt động khác nhau, và việc sử dụng sai có thể dẫn đến kết quả truy vấn không chính xác.

Ở đây, hãy làm rõ cách chúng khác biệt so với FIND_IN_SET và khi nào sử dụng từng cách tiếp cận.

So Sánh Với Mệnh Đề IN

Mệnh đề IN thường được sử dụng để kiểm tra xem một giá trị có khớp với một trong nhiều giá trị hằng số hay không.

-- Example of IN (this does NOT search inside "favorite_ids" for the value 3)
SELECT * FROM users WHERE favorite_ids IN ('3');

Trong trường hợp này, chỉ các bản ghi mà favorite_ids khớp chính xác với “3” mới được trả về. Điều đó có nghĩa là các giá trị như "1,3,5" sẽ không khớp—chỉ một hàng mà giá trị cột chính xác là "3" mới khớp.

Ngược lại, FIND_IN_SET kiểm tra vị trí của một phần tử trong danh sách phân cách bằng dấu phẩy, cho phép bạn lấy chính xác các bản ghi bao gồm “3” như thế này:

SELECT * FROM users WHERE FIND_IN_SET('3', favorite_ids);

Hướng Dẫn Sử Dụng Chính:

  • IN : Sử dụng với các bảng chuẩn hóa (ví dụ: SELECT * FROM posts WHERE category_id IN (1, 3, 5) )
  • FIND_IN_SET : Sử dụng với các chuỗi phân cách bằng dấu phẩy không chuẩn hóa

So Sánh Với Mệnh Đề LIKE

Về mặt kỹ thuật, bạn có thể sử dụng LIKE cho việc khớp một phần, nhưng nó đi kèm với những sai lầm quan trọng.

-- A common mistake with LIKE
SELECT * FROM users WHERE favorite_ids LIKE '%3%';

Truy vấn này không thực sự có nghĩa là “chứa giá trị 3”—nó khớp với bất kỳ chuỗi nào chứa ký tự “3”, nghĩa là nó có thể khớp sai với "13", "23", hoặc "30".

Điều này làm cho việc phát hiện đáng tin cậy xem 3 có tồn tại như một giá trị độc lập hay không trở nên không thể.

Hướng Dẫn Sử Dụng Chính:

  • LIKE : Hữu ích cho các tìm kiếm văn bản mờ, nhưng không thể nhận diện ranh giới phân cách bằng dấu phẩy
  • FIND_IN_SET : Kiểm tra chính xác các khớp giá trị độc lập bên trong danh sách phân cách bằng dấu phẩy

Sự Khác Biệt Về Hiệu Suất

MethodUses IndexSearch TargetSpeed
INYesNumber or single value◎ Very fast
LIKEDepends on patternText scan△ Can become slow depending on conditions
FIND_IN_SETNoFull scan× May be slow

Đặc biệt, FIND_IN_SET không thể sử dụng chỉ mục và thường kích hoạt quét toàn bộ bảng. Nếu bạn đang làm việc với các tập dữ liệu lớn, bạn có thể cần suy nghĩ lại về lược đồ.

5. Các Ghi Chú Quan Trọng Và Thực Tiễn Tốt Nhất

Không Tương Thích Với Các Giá Trị Chứa Dấu Phẩy

Hàm FIND_IN_SET giả định một danh sách đơn giản các giá trị được phân cách bằng dấu phẩy. Do đó, nếu một phần tử riêng lẻ trong danh sách chứa dấu phẩy, hàm sẽ không hoạt động như mong đợi.

Ví Dụ Không Đúng:

SELECT FIND_IN_SET('1,2', '1,2,3,4'); -- Result: 1

Dùng theo cách này có thể tạo ra các khớp không chính xác vì toàn bộ chuỗi được đánh giá không đúng cách.
Bạn chỉ nên sử dụng hàm này khi có thể đảm bảo rằng các giá trị riêng lẻ không chứa dấu phẩy.

Các Vấn Đề Về Hiệu Suất

FIND_IN_SET không thể sử dụng chỉ mục, nó thực hiện quét toàn bộ bảng. Kết quả là, khi sử dụng trên các bảng lớn, hiệu suất truy vấn có thể giảm đáng kể.

Các Giải Pháp Thay Thế:

  • Thay vì lưu trữ các giá trị phân cách bằng dấu phẩy, chuẩn hóa mối quan hệ và quản lý nó trong một bảng riêng biệt.
  • Trong các môi trường quan trọng về hiệu suất, hãy xem xét mở rộng bảng tạm thời hoặc các chiến lược dựa trên JOIN .

Ví dụ, nếu bạn tạo một bảng trung gian như user_favorites, bạn có thể tận dụng chỉ mục để tìm kiếm nhanh hơn:

SELECT users.*
FROM users
JOIN user_favorites ON users.id = user_favorites.user_id
WHERE user_favorites.favorite_id = 3;

Khả Năng Đọc Và Duy Trì

Mặc dù FIND_IN_SET có vẻ tiện lợi, nhưng nó đi kèm với một số nhược điểm:

  • Các truy vấn không trực quan (nó trả về giá trị vị trí)
  • Việc thêm hoặc xóa giá trị là rườm rà
  • Việc thực thi tính toàn vẹn dữ liệu là khó khăn (nhiều ý nghĩa trong một cột duy nhất)

Do đó, khi khả năng duy trì và tính toàn vẹn dữ liệu quan trọng, sửa đổi chính schema thường là thực hành tốt nhất.

Khi Bạn Phải Sử Dụng FIND_IN_SET

Có những tình huống mà bạn không có lựa chọn nào khác ngoài việc làm việc với các cột phân cách bằng dấu phẩy—như hệ thống cũ hoặc sản phẩm bên thứ ba. Trong những trường hợp như vậy, hãy xem xét các biện pháp phòng ngừa sau:

  • Áp dụng các điều kiện lọc khác trước để giảm phạm vi tìm kiếm
  • Ngăn chặn lỗi định dạng như dấu phẩy kép hoặc khoảng trắng đầu/cuối
  • Thực hiện xử lý bổ sung ở lớp ứng dụng khi có thể

6. Câu Hỏi Thường Gặp (FAQ)

FIND_IN_SET Có Thể Sử Dụng Chỉ Mục Không?

Không, FIND_IN_SET không thể sử dụng chỉ mục. Bên trong, nó tách và đánh giá chuỗi, vì vậy nó không hưởng lợi từ tối ưu hóa chỉ mục của MySQL.

Kết quả là, sử dụng nó trên các bảng lớn có thể làm chậm hiệu suất truy vấn. Đối với các hệ thống quan trọng về hiệu suất, hãy xem xét thiết kế lại schema hoặc chuẩn hóa dữ liệu.

Nó Có Hoạt Động Đúng Với Số Và Chuỗi Hỗn Hợp Không?

Nói chung, có—nhưng hãy nhớ rằng so sánh được thực hiện dưới dạng chuỗi. Nếu giá trị số và chuỗi được trộn lẫn, hành vi không mong đợi có thể xảy ra.

Ví dụ, cả hai câu sau đều trả về khớp cho 3:

SELECT FIND_IN_SET(3, '1,2,3,4');     -- Result: 3
SELECT FIND_IN_SET('3', '1,2,3,4');   -- Result: 3

Tuy nhiên, trong các trường hợp như FIND_IN_SET('03', '01,02,03'), định dạng số không có thể ảnh hưởng đến hành vi khớp.
An toàn nhất là chuẩn hóa định dạng giá trị.

Làm Thế Nào Để Tìm Kiếm Nhiều Giá Trị Cùng Lúc?

FIND_IN_SET chỉ chấp nhận một giá trị tìm kiếm duy nhất, nếu bạn muốn tìm kiếm các bản ghi chứa “3 hoặc 4,” bạn phải gọi nó nhiều lần sử dụng OR:

SELECT * FROM users
WHERE FIND_IN_SET('3', favorite_ids)
   OR FIND_IN_SET('4', favorite_ids);

Nếu các điều kiện trở nên phức tạp hơn, hãy xem xét xây dựng SQL động ở lớp ứng dụng hoặc di chuyển sang cấu trúc bảng chuẩn hóa.

FIND_IN_SET Đang Gây Ra Vấn Đề Hiệu Suất. Tôi Nên Làm Gì?

Các chiến lược sau là hiệu quả:

  • Chuyển sang thiết kế bảng chuẩn hóa
  • Áp dụng các điều kiện lọc trước để giảm phạm vi tìm kiếm
  • Chỉ sử dụng nó khi xử lý các tập dữ liệu nhỏ
  • Xem xét di chuyển sang các định dạng có cấu trúc như tìm kiếm toàn văn hoặc kiểu dữ liệu JSON

Modern MySQL versions support JSON data types. For example, if you manage the roles column as a JSON array, you can use JSON_CONTAINS() for flexible and efficient searches.

FIND_IN_SET Có Sẽ Bị Bỏ Qua Trong Tương Lai Không?

Từ MySQL 8.0, FIND_IN_SET chưa bị chính thức loại bỏ. Tuy nhiên, cấu trúc dữ liệu không chuẩn hóa (cột phân cách bằng dấu phẩy) không được khuyến nghị, vì vậy việc sử dụng thực tế của hàm này dự kiến sẽ giảm dần theo thời gian.

Khi thiết kế lại cơ sở dữ liệu của bạn, lý tưởng nhất là áp dụng cấu trúc chuẩn hóa hoặc thiết kế dựa trên JSON.

7. Kết Luận

Ôn Lại Các Tính Năng Và Lợi Ích Của FIND_IN_SET

Hàm FIND_IN_SET cực kỳ hữu ích trong MySQL khi tìm kiếm chuỗi phân cách bằng dấu phẩy. Nó đặc biệt hữu ích khi bạn cần trích xuất các bản ghi chứa một giá trị cụ thể trong một cột duy nhất lưu trữ nhiều giá trị.

Với cú pháp đơn giản, nó cho phép kiểm tra khớp giá trị độc lập mà khó đạt được chính xác với các mệnh đề LIKE hoặc IN. Khả năng phát hiện các phần tử riêng biệt trong danh sách phân cách bằng dấu phẩy là điểm mạnh lớn nhất của nó.

Những Lưu Ý Quan Trọng Khi Sử Dụng Nó

Đồng thời, có một số hạn chế và lưu ý quan trọng, vì vậy không nên lạm dụng mà không suy nghĩ cẩn thận:

  • Không thể sử dụng chỉ mục (có thể làm chậm tìm kiếm)
  • Không tương thích với các giá trị chứa dấu phẩy
  • Giả định cấu trúc không chuẩn hóa
  • Chỉ hỗ trợ tìm kiếm giá trị đơn (tìm kiếm nhiều yêu cầu điều kiện OR)

Hiểu các đặc tính này là cần thiết để sử dụng hàm một cách phù hợp.

Khi Nào Bạn Nên — Và Không Nên — Sử Dụng Nó

SituationShould You Use It?Reason
Small dataset, infrequent searches✅ YesEasy to implement and low development cost
Dependent on a legacy system structure✅ Use selectivelyUseful when refactoring is difficult
Large dataset, high-frequency access❌ Not recommendedPerformance degradation becomes significant
Schema can be normalized❌ AvoidJOINs or intermediate tables are more efficient

Cách Áp Dụng Điều Này Trong Thực Tế

  • Hiểu nó như một công cụ linh hoạt để làm việc trong các cấu trúc cơ sở dữ liệu hiện có
  • Sử dụng nó như một điểm tham chiếu khi quyết định có áp dụng thiết kế dữ liệu chuẩn hóa trong tương lai không
  • Thay vì sử dụng nó như một giải pháp nhanh, hãy hiểu rõ hàm thực sự làm gì

Đối với các nhà phát triển ưu tiên khả năng bảo trì và tính dễ đọc, tốt nhất là nghĩ về điều này như một hàm mà bạn có thể “sử dụng tạm thời—nhưng cuối cùng sẽ tốt nghiệp khỏi nó.”