ปัญหา
คอลัมน์เก็บค่าหลายค่าคั่นด้วยจุลภาค (เช่น '23,45,78,56') ไม่สามารถใช้ IN() ได้โดยตรง เพราะ IN() คาดหวังรายการค่า ไม่ใช่สตริงเดียว
วิธีแก้: ใช้ FIND_IN_SET()
FIND_IN_SET(needle, haystack)
-- คืนค่า Position (1-based) ถ้าพบ, 0 ถ้าไม่พบ
ตัวอย่างสมบูรณ์
1. สร้างตารางและข้อมูล
CREATE TABLE DemoTable (`Values` VARCHAR(50));
INSERT INTO DemoTable VALUES ('23,45,78,56');
INSERT INTO DemoTable VALUES ('384,476,7456');
INSERT INTO DemoTable VALUES ('68,8,88,89');
INSERT INTO DemoTable VALUES ('78,80,84');
2. ค้นหาแถวที่มีค่า '8' ในรายการ
SELECT * FROM DemoTable
WHERE FIND_IN_SET('8', `Values`) != 0;
3. ผลลัพธ์
+------------+
| Values |
+------------+
| 68,8,88,89 |
+------------+
รายละเอียด FIND_IN_SET()
| พารามิเตอร์ | คำอธิบาย |
|---|---|
| needle | ค่าที่ต้องการค้นหา (String) |
| haystack | สตริงรายการคั่นจุลภาค (Comma-separated) |
| Return | ตำแหน่ง (1,2,3...) หากพบ, 0 หากไม่พบ, NULL หากอาร์กิวเมนต์เป็น NULL |
ข้อจำกัดและข้อควรระวัง
- ไม่ใช้ Index → Full Table Scan เสมอ
- คั่นต้องเป็น จุลภาค (,) เท่านั้น ไม่รองรับ Space, Semicolon, Pipe
- ค่าที่มีจุลภาคภายใน (เช่น
'1,2', '3,4') จะทำงานผิด - Case-sensitive ขึ้นกับ Collation ของคอลัมน์
แนวทางปฏิบัติที่ดีกว่า (Best Practice)
Normalize ข้อมูล: สร้างตารางความสัมพันธ์ (Many-to-Many)
CREATE TABLE Items (
Id INT PRIMARY KEY,
Name VARCHAR(50)
);
CREATE TABLE ItemTags (
ItemId INT,
Tag VARCHAR(50),
PRIMARY KEY (ItemId, Tag),
FOREIGN KEY (ItemId) REFERENCES Items(Id)
);
-- แทนที่จะเก็บ 'tag1,tag2,tag3' ในคอลัมน์เดียว
Query แบบ Normalized (เร็ว ใช้ Index ได้)
SELECT i.* FROM Items i
JOIN ItemTags it ON i.Id = it.ItemId
WHERE it.Tag = '8';
กรณีที่ยอมรับ FIND_IN_SET() ได้
- ตารางเล็ก (< 1000 แถว)
- ข้อมูลไม่เปลี่ยนบ่อย (Read-heavy)
- Legacy System ที่แก้ Schema ไม่ได้
- Ad-hoc Query ชั่วคราว
สรุป
ใช้ FIND_IN_SET(value, csv_column) เพื่อค้นหาค่าในสตริงคั่นจุลภาค คืนค่า > 0 = พบ, 0 = ไม่พบ แต่ไม่ใช้ Index ควร Normalize ข้อมูลเพื่อประสิทธิภาพยาวนาน