สถานการณ์: คอลัมน์เก็บรายชื่อคั่น Comma
คอลัมน์ username VARCHAR(200) เก็บ "John,Chris,David"
ฟังก์ชัน FIND_IN_SET()
FIND_IN_SET(needle, haystack) -- คืนตำแหน่ง (1-based) หรือ 0 ถ้าไม่พบ
ตัวอย่าง: ตาราง demo81
CREATE TABLE demo81 (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(200)
);
INSERT INTO demo81 (username) VALUES
('John,Chris,David'), ('Mike,Sam'), ('Chris,Bob,Sam'), ('Mike,John,Chris');
ข้อมูล
+----+------------------+ | id | username | +----+------------------+ | 1 | John,Chris,David | | 2 | Mike,Sam | | 3 | Chris,Bob,Sam | | 4 | Mike,John,Chris | +----+------------------+
คำสั่ง: หาแถวที่มี 'Chris'
SELECT * FROM demo81
WHERE FIND_IN_SET('Chris', username) > 0;
ผลลัพธ์ (3 แถว)
+----+------------------+ | id | username | +----+------------------+ | 1 | John,Chris,David | | 3 | Chris,Bob,Sam | | 4 | Mike,John,Chris | +----+------------------+
ทำไมไม่ใช้ LIKE?
| Query | Match 'Chris' | Match 'Chris1' (False +) |
|---|---|---|
| LIKE '%Chris%' | ✅ | ❌ Match |
| FIND_IN_SET('Chris', col) | ✅ | ✅ ไม่ Match |
ข้อจำกัด FIND_IN_SET
- Delimiter ต้องเป็น Comma (,) เท่านั้น
- ไม่รองรับ Space: 'Chris, Bob' → ไม่ Match 'Bob' (ต้อง 'Chris,Bob')
- NULL Input → คืน NULL
- ไม่ใช้ INDEX ได้
- haystack > 64KB อาจมีปัญหา (Version ขึ้นอยู่)
แก้ Space: REPLACE ก่อน
WHERE FIND_IN_SET('Bob', REPLACE(username, ', ', ',')) > 0;
แนวทางแทนที่ (Best Practice)
1. Normalized Table (แนะนำมากที่สุด)
CREATE TABLE user_tags (
user_id INT,
tag VARCHAR(50),
PRIMARY KEY (user_id, tag)
);
2. JSON Column (MySQL 5.7+)
ALTER TABLE demo81 ADD tags JSON;
UPDATE demo81 SET tags = JSON_ARRAY('John','Chris','David') WHERE id = 1;
SELECT * FROM demo81 WHERE JSON_CONTAINS(tags, '"Chris"');
3. Generated Column + Index
ALTER TABLE demo81
ADD COLUMN has_chris BOOLEAN AS (FIND_IN_SET('Chris', username) > 0) STORED,
ADD INDEX idx_has_chris (has_chris);
Dynamic IN List (Application Layer)
ใน Application: Parse String → List → ใช้ IN (?) พร้อม Bind Parameters
String sql = "SELECT * FROM demo81 WHERE FIND_IN_SET(?, username) > 0"; ps.setString(1, "Chris");