Vấn đề
Toán tử IN không hoạt động trực tiếp với chuỗi phân cách bằng dấu phẩy (CSV) trong một cột hoặc biến.
Giải pháp: FIND_IN_SET()
Cú pháp: FIND_IN_SET(str, csv_string) - trả về vị trí (1-based) của str trong csv_string, trả về 0 nếu không tìm thấy.
Ví dụ
Tạo bảng:
CREATE TABLE DemoTable604 (
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Title VARCHAR(100)
);
INSERT INTO DemoTable604(Title) VALUES
('MySQL'), ('C++'), ('MongoDB'), ('Java');
Truy vấn tìm Title trong danh sách 'C++,MongoDB':
SELECT * FROM DemoTable604
WHERE FIND_IN_SET(Title, 'C++,MongoDB');
Kết quả:
+----+---------+
| Id | Title |
+----+---------+
| 2 | C++ |
| 3 | MongoDB |
+----+---------+
So sánh IN vs FIND_IN_SET
| Đặc điểm | IN (value1, value2...) | FIND_IN_SET(col, 'csv') |
|---|---|---|
| Đối số | Danh sách giá trị rời rạc | 1 chuỗi CSV |
| Sử dụng index | Có (nếu cột có index) | Không (full scan) |
| Ký tự đặc biệt trong giá trị | Dễ dàng (quote riêng) | Khó (dấu phẩy trong giá trị gây lỗi) |
| Hiệu năng | Tốt | Kém (function trên cột) |
Khi nào dùng FIND_IN_SET
- Dữ liệu lưu dưới dạng CSV trong 1 cột (bad design nhưng phổ biến legacy).
- Tham số truyền vào từ ứng dụng dạng CSV string.
- Bảng nhỏ, không quan tâm index.
Thiết kế tốt hơn
Thay vì lưu CSV, hãy dùng bảng liên kết (many-to-many):
CREATE TABLE items (id INT PRIMARY KEY, name VARCHAR(100));
CREATE TABLE tags (id INT PRIMARY KEY, name VARCHAR(100));
CREATE TABLE item_tags (item_id INT, tag_id INT, PRIMARY KEY(item_id, tag_id));
Truy vấn: SELECT i.* FROM items i JOIN item_tags it ON i.id=it.item_id WHERE it.tag_id IN (1,2,3).