1. บทนำ
ความท้าทายในการค้นหาข้อมูลที่คั่นด้วยเครื่องหมายคอมม่าใน MySQL
เมื่อทำงานกับฐานข้อมูล คุณอาจเจอกรณีที่ค่าหลายค่าเก็บอยู่ในคอลัมน์เดียวโดยคั่นด้วยเครื่องหมายคอมม่า ตัวอย่างเช่น คอลัมน์อาจมีสตริงเช่น "1,3,5" และคุณอาจต้องการดึงเฉพาะเรคคอร์ดที่มีค่า “3” อยู่ในนั้น
ในกรณีเช่นนี้ การใช้ตัวดำเนินการ = มาตรฐานหรือเงื่อนไข IN มักจะไม่ให้ผลลัพธ์ตามที่คาดหวัง เนื่องจากสตริงที่คั่นด้วยคอมม่าได้รับการพิจารณาเป็นค่าเดียวทั้งหมด หมายความว่าการเปรียบเทียบจะทำกับสตริงทั้งหมด ไม่ใช่องค์ประกอบแต่ละตัวภายในสตริงนั้น
FIND_IN_SET คืออะไร?
ในสถานการณ์เช่นนี้ ฟังก์ชัน FIND_IN_SET ของ MySQL จะมีประโยชน์อย่างมาก
ฟังก์ชันนี้ช่วยให้คุณตรวจสอบได้อย่างง่ายดายว่าค่าที่ระบุมีอยู่ในสตริงที่คั่นด้วยคอมม่าหรือไม่
ตัวอย่างเช่น พิจารณาคำสั่ง SQL ต่อไปนี้:
SELECT * FROM users WHERE FIND_IN_SET('3', favorite_ids);
ในคิวรีนี้ คุณสามารถดึงเรคคอร์ดที่สตริงที่คั่นด้วยคอมม่าในคอลัมน์ favorite_ids (เช่น "1,2,3,4") มีค่า “3” อยู่ด้วย
จุดประสงค์ของบทความนี้และกลุ่มเป้าหมาย
บทความนี้อธิบายวิธีใช้ฟังก์ชัน FIND_IN_SET ตั้งแต่พื้นฐานอย่างชัดเจนและเป็นระบบ ตั้งแต่ไวยากรณ์พื้นฐานจนถึงตัวอย่างการใช้งานจริง การเปรียบเทียบกับวิธีการค้นหาอื่น ๆ ข้อควรระวังสำคัญ และคำถามที่พบบ่อย คู่มือฉบับนี้ให้ ความรู้เชิงปฏิบัติสำหรับการพัฒนาในโลกจริง
บทความนี้มุ่งเน้นไปที่:
- วิศวกรเว็บและนักพัฒนาฝั่งเซิร์ฟเวอร์ที่ใช้ MySQL เป็นประจำ
- นักพัฒนาที่ต้องทำงานกับระบบที่มีอยู่ซึ่งเก็บข้อมูลเป็นคอมม่า‑เซพาเรต
- ผู้เริ่มต้น SQL ที่ประสบปัญหาเรื่องการจับคู่บางส่วนและการค้นหาตามค่า
2. ไวยากรณ์พื้นฐานและพฤติกรรมของฟังก์ชัน FIND_IN_SET
ไวยากรณ์ของ FIND_IN_SET
FIND_IN_SET เป็นฟังก์ชันของ MySQL ที่ใช้ตรวจสอบว่าค่าที่ระบุมีอยู่ในสตริงที่คั่นด้วยคอมม่าหรือไม่ ไวยากรณ์พื้นฐานมีดังนี้:
FIND_IN_SET(search_value, comma_separated_string)
ตัวอย่าง:
SELECT FIND_IN_SET('3', '1,2,3,4'); -- Result: 3
ในตัวอย่างนี้ เนื่องจาก “3” ปรากฏในตำแหน่งที่สาม ฟังก์ชันจะคืนค่าเชิงตัวเลข 3
กฎการคืนค่า
ฟังก์ชัน FIND_IN_SET ทำงานตามกฎต่อไปนี้:
| Condition | Result |
|---|---|
| The search value exists in the list | Its position in the list (starting from 1) |
| The search value does not exist | 0 |
| Either argument is NULL | NULL |
ตัวอย่าง (คืนตำแหน่ง)
SELECT FIND_IN_SET('b', 'a,b,c'); -- Result: 2
ตัวอย่าง (ไม่พบค่า)
SELECT FIND_IN_SET('d', 'a,b,c'); -- Result: 0
ตัวอย่าง (รวม NULL)
SELECT FIND_IN_SET(NULL, 'a,b,c'); -- Result: NULL
ตัวอย่างการใช้งานในเงื่อนไข WHERE
ฟังก์ชันนี้มักใช้สำหรับการกรองข้อมูลภายในเงื่อนไข WHERE
SELECT * FROM users WHERE FIND_IN_SET('admin', roles);
ในตัวอย่างนี้ จะคืนเฉพาะแถวที่คอลัมน์ roles มีสตริง “admin” หากคอลัมน์มีค่าเช่น "user,editor,admin" ก็จะตรงกัน
หมายเหตุสำคัญเกี่ยวกับตัวเลขและสตริง
FIND_IN_SET ทำการเปรียบเทียบ เป็นสตริง ซึ่งหมายความว่ามันทำงานดังนี้:
SELECT FIND_IN_SET(3, '1,2,3,4'); -- Result: 3
SELECT FIND_IN_SET('3', '1,2,3,4'); -- Result: 3
แม้ว่าจะทำงานได้กับค่าตัวเลขและสตริง แต่ประเภทข้อมูลที่ไม่ชัดเจนอาจทำให้พฤติกรรมไม่คาดคิด ดังนั้นจึงแนะนำให้จัดการค่าด้วยการแปลงเป็นสตริงอย่างชัดเจนเสมอเมื่อเป็นไปได้
3. ตัวอย่างการใช้งานจริง
การค้นหาในคอลัมน์ที่เก็บสตริงคั่นด้วยคอมม่า
ในระบบจริง คุณอาจพบกรณีที่หลายค่า (เช่น ID หรือสิทธิ์) ถูกเก็บในคอลัมน์เดียวเป็นสตริงคั่นด้วยคอมม่า ตัวอย่างเช่น ตาราง users ด้านล่างนี้
| id | name | favorite_ids |
|---|---|---|
| 1 | Taro | 1,3,5 |
| 2 | Hanako | 2,4,6 |
| 3 | Jiro | 3,4,5 |
เมื่อคุณต้องการ “ดึงผู้ใช้ที่มีค่า 3 อยู่ในรายการ” ฟังก์ชัน FIND_IN_SET จะเป็นเครื่องมือที่สะดวกมาก
SELECT * FROM users WHERE FIND_IN_SET('3', favorite_ids);
การรัน SQL นี้จะคืนเรคคอร์ดของ “Taro” และ “Jiro”.
ทำงานได้ดีแม้ค่าดูเหมือนเป็นตัวเลข
แม้ว่า favorite_ids จะดูเหมือนมีตัวเลข, FIND_IN_SET ทำการเปรียบเทียบ แบบสตริง ดังนั้นจึงปลอดภัยที่สุดที่จะส่งอาร์กิวเมนต์เป็นสตริงพร้อมเครื่องหมายคำพูด.
-- 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);
เพื่อให้คิวรีอ่านง่ายและพฤติกรรมคาดเดาได้, แนะนำให้ระบุค่าเป็นสตริงอย่างชัดเจน.
การค้นหาแบบไดนามิก (ตัวแปรและตัวแทน)
เมื่อสร้าง SQL แบบไดนามิกจากเว็บแอปพลิเคชัน, มักจะใช้ตัวแปรหรือพารามิเตอร์ที่ผูกไว้.
หากคุณใช้ตัวแปร MySQL, จะมีลักษณะดังนี้:
SET @target_id = '3';
SELECT * FROM users WHERE FIND_IN_SET(@target_id, favorite_ids);
เมื่อผูกค่าจากชั้นแอปพลิเคชัน (เช่น PHP, Python, หรือ Node.js), คุณสามารถจัดการได้เช่นเดียวกันโดยใช้ตัวแแทน.
วิธีจัดการการค้นหาหลายค่า
น่าเสียดายที่ FIND_IN_SET สามารถ ค้นหาได้เพียงค่าเดียวต่อครั้ง.
หากคุณต้องการดึงเรคคอร์ดที่มี “3 หรือ 4”, คุณต้องเขียนหลายครั้งโดยใช้ OR.
SELECT * FROM users
WHERE FIND_IN_SET('3', favorite_ids) OR FIND_IN_SET('4', favorite_ids);
หากเงื่อนไขซับซ้อนขึ้น, คุณควรสร้าง SQL แบบไดนามิกในแอปพลิเคชันของคุณหรือพิจารณาย้ายไปใช้โครงสร้างตารางที่ทำให้เป็นปกติ (normalized).
4. การเปรียบเทียบ FIND_IN_SET กับวิธีการค้นหาอื่น
ตัวเลือกทั่วไป: IN และ LIKE
ใน MySQL, นอกจาก FIND_IN_SET แล้ว คุณอาจเห็นการใช้คำสั่ง IN หรือ LIKE เพื่อตรวจสอบว่าค่าถูกใส่หรือไม่. อย่างไรก็ตามแต่ละวิธีทำงานแตกต่างกัน, และการใช้วิธีที่ไม่ถูกต้องอาจทำให้ได้ ผลลัพธ์คิวรีที่ไม่ถูกต้อง.
ที่นี่เราจะอธิบายความแตกต่างจาก FIND_IN_SET และเมื่อควรใช้แต่ละวิธี.
การเปรียบเทียบกับคำสั่ง IN
คำสั่ง IN มักใช้เพื่อตรวจสอบว่าค่าตรงกับ หนึ่งในหลายค่าคงที่.
-- Example of IN (this does NOT search inside "favorite_ids" for the value 3)
SELECT * FROM users WHERE favorite_ids IN ('3');
ในกรณีนี้, จะคืนค่าเฉพาะเรคคอร์ดที่ favorite_ids ตรงกันอย่างเต็มที่ กับ “3”. หมายความว่าค่าเช่น "1,3,5" จะไม่ตรง—จะตรงเฉพาะแถวที่ค่าคอลัมน์เป็น "3" เท่านั้น.
ในทางตรงกันข้าม, FIND_IN_SET ตรวจสอบตำแหน่งขององค์ประกอบในรายการคั่นด้วยคอมม่า, ทำให้คุณดึงเรคคอร์ดที่มี “3” อย่างแม่นยำได้ดังนี้:
SELECT * FROM users WHERE FIND_IN_SET('3', favorite_ids);
✅ แนวทางการใช้งานหลัก:
IN: ใช้กับตารางที่ทำให้เป็นปกติ (เช่นSELECT * FROM posts WHERE category_id IN (1, 3, 5))FIND_IN_SET: ใช้กับสตริงคั่นด้วยคอมม่าแบบไม่ทำให้เป็นปกติ
การเปรียบเทียบกับคำสั่ง LIKE
โดยเทคนิค, คุณสามารถใช้ LIKE เพื่อทำการจับคู่บางส่วน, แต่ก็มีข้อเสียสำคัญ.
-- A common mistake with LIKE
SELECT * FROM users WHERE favorite_ids LIKE '%3%';
คิวรีนี้ไม่ได้หมายความว่า “มีค่า 3 อยู่” จริง ๆ — มันจะจับคู่กับสตริงใดก็ได้ที่มีอักขระ “3”, ซึ่งอาจทำให้จับคู่กับ "13", "23" หรือ "30" อย่างไม่ถูกต้อง.
ทำให้ไม่สามารถตรวจจับได้อย่างเชื่อถือได้ว่า 3 มีอยู่เป็นค่าที่แยกจากกัน.
✅ แนวทางการใช้งานหลัก:
LIKE: มีประโยชน์สำหรับการค้นหาข้อความแบบคลุมเครือ, แต่ไม่สามารถจำกัดขอบเขตคอมม่าได้FIND_IN_SET: ตรวจสอบค่าที่แยกจากกันในรายการคั่นด้วยคอมม่าอย่างแม่นยำ
ความแตกต่างด้านประสิทธิภาพ
| Method | Uses Index | Search Target | Speed |
|---|---|---|---|
IN | Yes | Number or single value | ◎ Very fast |
LIKE | Depends on pattern | Text scan | △ Can become slow depending on conditions |
FIND_IN_SET | No | Full scan | × May be slow |
โดยเฉพาะ, FIND_IN_SET ไม่สามารถใช้ดัชนีและมักทำให้เกิดการสแกนตารางเต็ม. หากคุณทำงานกับชุดข้อมูลขนาดใหญ่, คุณอาจต้องพิจารณาออกแบบสคีม่าใหม่.
5. หมายเหตุสำคัญและแนวปฏิบัติที่ดีที่สุด
ไม่รองรับค่าที่มีเครื่องหมายจุลภาค
ฟังก์ชัน FIND_IN_SET สมมติว่าเป็น รายการค่าที่คั่นด้วยเครื่องหมายคอมม่าแบบง่าย ดังนั้น หากองค์ประกอบใดในรายการเองมีคอมม่า ฟังก์ชันจะไม่ทำงานตามที่คาดหวัง
ตัวอย่างที่ไม่ถูกต้อง:
SELECT FIND_IN_SET('1,2', '1,2,3,4'); -- Result: 1
การใช้แบบนี้อาจทำให้ได้ผลลัพธ์ที่ไม่ถูกต้อง เพราะสตริงทั้งหมดถูกประเมินอย่างไม่เหมาะสม
คุณควรใช้ฟังก์ชันนี้เฉพาะเมื่อคุณสามารถรับประกันว่า ค่าต่าง ๆ ไม่มีคอมม่า เท่านั้น.
ความกังวลเรื่องประสิทธิภาพ
เนื่องจาก FIND_IN_SET ไม่สามารถใช้ดัชนีได้ มันจึงทำการ สแกนตารางเต็ม ผลที่ตามมาคือ เมื่อใช้กับตารางขนาดใหญ่ ประสิทธิภาพของคิวรีอาจลดลงอย่างมาก
วิธีแก้ไข:
- แทนการเก็บค่าที่คั่นด้วยคอมม่า ให้ ทำให้ความสัมพันธ์เป็นแบบปกติ และจัดการในตารางแยกต่างหาก
- ในสภาพแวดล้อมที่ต้องการประสิทธิภาพสูง ควรพิจารณา การขยายตารางชั่วคราว หรือ กลยุทธ์แบบใช้ JOIN
ตัวอย่างเช่น หากคุณสร้างตารางชั่วคราวเช่น user_favorites คุณสามารถใช้ประโยชน์จากดัชนีเพื่อการค้นหาที่เร็วขึ้น:
SELECT users.*
FROM users
JOIN user_favorites ON users.id = user_favorites.user_id
WHERE user_favorites.favorite_id = 3;
ความอ่านง่ายและการบำรุงรักษา
แม้ว่า FIND_IN_SET จะดูสะดวก แต่ก็มีข้อเสียหลายประการ:
- คำสั่งคิวรีไม่เป็นธรรมชาติ (มันคืนค่าตำแหน่ง)
- การเพิ่มหรือการลบค่าเป็นเรื่องยุ่งยาก
- ความสมบูรณ์ของข้อมูลยากต่อการบังคับใช้ (มีหลายความหมายในคอลัมน์เดียว)
ดังนั้นเมื่อความสามารถในการบำรุงรักษาและความสมบูรณ์ของข้อมูลเป็นสิ่งสำคัญ, การปรับโครงสร้างสกีมานั้นมักเป็นแนวปฏิบัติที่ดีที่สุด.
เมื่อคุณต้องใช้ FIND_IN_SET
มีบางสถานการณ์ที่คุณไม่มีทางเลือกอื่นนอกจากทำงานกับคอลัมน์ที่คั่นด้วยเครื่องหมายคอมม่า—เช่นระบบเก่าหรือผลิตภัณฑ์ของบุคคลที่สาม ในกรณีเช่นนั้น ให้พิจารณาข้อควรระวังต่อไปนี้:
- ใช้เงื่อนไขการกรองอื่น ๆ ก่อนเพื่อลดขอบเขตการค้นหา
- ป้องกันข้อผิดพลาดการจัดรูปแบบ เช่น คอมม่าใส่ซ้ำหรือช่องว่างที่ต้นหรือท้าย
- ทำการประมวลผลเสริมที่ระดับแอปพลิเคชันเมื่อเป็นไปได้
6. คำถามที่พบบ่อย (FAQ)
FIND_IN_SET สามารถใช้ดัชนีได้หรือไม่?
ไม่, FIND_IN_SET ไม่สามารถใช้ดัชนีได้. ภายในมันจะแยกและประเมินสตริง, ดังนั้นจึงไม่ได้รับประโยชน์จากการปรับแต่งดัชนีของ MySQL.
ผลที่ตามมาคือ การใช้บนตารางขนาดใหญ่อาจ ทำให้ประสิทธิภาพการคิวรีช้าลง. สำหรับระบบที่ต้องการประสิทธิภาพสูง ควรพิจารณาออกแบบสกีม่าใหม่หรือทำให้ข้อมูลเป็นแบบปกติ (normalize).
มันทำงานอย่างถูกต้องกับตัวเลขและสตริงที่ผสมกันหรือไม่?
โดยทั่วไปแล้ว ใช่—แต่ควรจำว่า การเปรียบเทียบจะทำเป็นสตริง. หากค่าตัวเลขและสตริงผสมกัน อาจเกิดพฤติกรรมที่ไม่คาดคิด.
ตัวอย่างเช่น ทั้งสองกรณีต่อไปนี้จะคืนผลการจับคู่สำหรับ 3:
SELECT FIND_IN_SET(3, '1,2,3,4'); -- Result: 3
SELECT FIND_IN_SET('3', '1,2,3,4'); -- Result: 3
อย่างไรก็ตาม ในกรณีเช่น FIND_IN_SET('03', '01,02,03') การจัดรูปแบบเลขที่มีศูนย์นำหน้าอาจส่งผลต่อพฤติกรรมการจับคู่.
จึงปลอดภัยที่สุดที่จะ ทำให้รูปแบบค่ามีมาตรฐาน.
ฉันจะค้นหาหลายค่าในครั้งเดียวได้อย่างไร?
เนื่องจาก FIND_IN_SET ยอมรับ ค่าการค้นหาเดียวเท่านั้น, หากคุณต้องการค้นหารายการที่มี “3 หรือ 4,” คุณต้องเรียกใช้หลายครั้งโดยใช้ OR:
SELECT * FROM users
WHERE FIND_IN_SET('3', favorite_ids)
OR FIND_IN_SET('4', favorite_ids);
หากเงื่อนไขซับซ้อนขึ้น, ควรพิจารณาสร้าง SQL อย่างไดนามิกที่ระดับแอปพลิเคชันหรือย้ายไปใช้โครงสร้างตารางแบบปกติ.
FIND_IN_SET กำลังทำให้เกิดปัญหาประสิทธิภาพ ฉันควรทำอย่างไร?
กลยุทธ์ต่อไปนี้มีประสิทธิภาพ:
- เปลี่ยนไปใช้การออกแบบตารางแบบปกติ
- ใช้เงื่อนไขการกรองก่อนเพื่อลดขอบเขตการค้นหา
- ใช้เฉพาะเมื่อทำงานกับชุดข้อมูลขนาดเล็ก
- พิจารณาย้ายไปใช้รูปแบบโครงสร้างเช่นการค้นหาแบบเต็มข้อความหรือประเภทข้อมูล JSON
เวอร์ชัน MySQL สมัยใหม่รองรับประเภทข้อมูล JSON. ตัวอย่างเช่น หากคุณจัดการคอลัมน์ roles เป็นอาร์เรย์ JSON, คุณสามารถใช้ JSON_CONTAINS() เพื่อการค้นหาที่ยืดหยุ่นและมีประสิทธิภาพ.
FIND_IN_SET จะถูกลบการใช้งานในอนาคตหรือไม่?
ณ เวลา MySQL 8.0 FIND_IN_SET ยังไม่ถูกลบการใช้งานอย่างเป็นทางการ อย่างไรก็ตาม โครงสร้างข้อมูลที่ไม่เป็น normalized (คอลัมน์ที่คั่นด้วย comma) ไม่ได้รับการแนะนำ ดังนั้นการใช้งานจริงของฟังก์ชันนี้คาดว่าจะลดลงตามเวลา
เมื่อออกแบบฐานข้อมูลใหม่ ควรนำ โครงสร้าง normalized หรือการออกแบบที่ใช้ JSON มาใช้
7. สรุป
ทบทวนคุณสมบัติและข้อดีของ FIND_IN_SET
ฟังก์ชัน FIND_IN_SET มีประโยชน์อย่างยิ่งใน MySQL เมื่อค้นหา string ที่คั่นด้วย comma มันช่วยเหลือโดยเฉพาะเมื่อคุณต้องการดึงบันทึกที่มีค่าค่าหนึ่งในคอลัมน์เดียวที่เก็บค่าหลายค่า
ด้วยไวยากรณ์ที่เรียบง่าย มันช่วยให้ตรวจสอบ การจับคู่ค่าอิสระ ที่ยากที่จะทำได้อย่างแม่นยำด้วย LIKE หรือ IN clauses ความสามารถในการตรวจจับองค์ประกอบที่แตกต่างในรายการที่คั่นด้วย comma คือจุดแข็งที่ยิ่งใหญ่ที่สุด
ข้อควรพิจารณาสำคัญเมื่อใช้งาน
ในขณะเดียวกัน มี ข้อจำกัดและข้อควรพิจารณาสำคัญหลายประการ ดังนั้นไม่ควรใช้งานมากเกินไปโดยไม่คิดอย่างรอบคอบ:
- ไม่สามารถใช้ Indexes (ซึ่งอาจทำให้การค้นหาช้าลง)
- ไม่เข้ากันกับค่าที่มี comma
- สมมติโครงสร้างที่ไม่ normalized
- รองรับเฉพาะการค้นหาค่าเดียว (การค้นหาหลายค่าต้องใช้
ORconditions)
การเข้าใจลักษณะเหล่านี้จำเป็นสำหรับการใช้งานฟังก์ชันอย่างเหมาะสม
เมื่อควร — และไม่ควร — ใช้งาน
| Situation | Should You Use It? | Reason |
|---|---|---|
| Small dataset, infrequent searches | ✅ Yes | Easy to implement and low development cost |
| Dependent on a legacy system structure | ✅ Use selectively | Useful when refactoring is difficult |
| Large dataset, high-frequency access | ❌ Not recommended | Performance degradation becomes significant |
| Schema can be normalized | ❌ Avoid | JOINs or intermediate tables are more efficient |
วิธีนำไปใช้ในทางปฏิบัติ
- เข้าใจมันเป็นเครื่องมือที่ยืดหยุ่นสำหรับทำงานภายในโครงสร้างฐานข้อมูลที่มีอยู่
- ใช้มันเป็นจุดอ้างอิงเมื่อตัดสินใจว่าจะนำการออกแบบข้อมูล normalized มาใช้ในอนาคตหรือไม่
- แทนที่จะใช้มันเป็นการแก้ไขด่วน ให้เข้าใจชัดเจนว่าฟังก์ชันนี้ทำอะไรจริงๆ
สำหรับนักพัฒนาที่ให้ความสำคัญกับ ความสามารถในการบำรุงรักษาและความอ่านง่าย ควรคิดถึงฟังก์ชันนี้ว่าเป็นฟังก์ชันที่คุณอาจ “ใช้ชั่วคราว—แต่ในที่สุดจะเลิกใช้”


