อธิบาย MySQL FIND_IN_SET: วิธีค้นหาค่าที่คั่นด้วยเครื่องหมายจุลภาคอย่างถูกต้อง

目次

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 ทำงานตามกฎต่อไปนี้:

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

ตัวอย่าง (คืนตำแหน่ง)

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 ด้านล่างนี้

idnamefavorite_ids
1Taro1,3,5
2Hanako2,4,6
3Jiro3,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 : ตรวจสอบค่าที่แยกจากกันในรายการคั่นด้วยคอมม่าอย่างแม่นยำ

ความแตกต่างด้านประสิทธิภาพ

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

โดยเฉพาะ, 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
  • รองรับเฉพาะการค้นหาค่าเดียว (การค้นหาหลายค่าต้องใช้ OR conditions)

การเข้าใจลักษณะเหล่านี้จำเป็นสำหรับการใช้งานฟังก์ชันอย่างเหมาะสม

เมื่อควร — และไม่ควร — ใช้งาน

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

วิธีนำไปใช้ในทางปฏิบัติ

  • เข้าใจมันเป็นเครื่องมือที่ยืดหยุ่นสำหรับทำงานภายในโครงสร้างฐานข้อมูลที่มีอยู่
  • ใช้มันเป็นจุดอ้างอิงเมื่อตัดสินใจว่าจะนำการออกแบบข้อมูล normalized มาใช้ในอนาคตหรือไม่
  • แทนที่จะใช้มันเป็นการแก้ไขด่วน ให้เข้าใจชัดเจนว่าฟังก์ชันนี้ทำอะไรจริงๆ

สำหรับนักพัฒนาที่ให้ความสำคัญกับ ความสามารถในการบำรุงรักษาและความอ่านง่าย ควรคิดถึงฟังก์ชันนี้ว่าเป็นฟังก์ชันที่คุณอาจ “ใช้ชั่วคราว—แต่ในที่สุดจะเลิกใช้”