MySQL แบบสอบถามย่อยพร้อมตัวอย่าง

⚡ สรุปอย่างชาญฉลาด

MySQL ไวยากรณ์ของ SubQuery จะวางคำสั่ง SELECT หนึ่งไว้ภายในอีกคำสั่ง SELECT หนึ่ง เพื่อให้ผลลัพธ์จากคำสั่งภายในส่งไปยังคำสั่งภายนอก คำอธิบายนี้ครอบคลุมถึง SubQuery แบบ Scalar, Row และ Table ลำดับการทำงาน ตัวอย่างการใช้งานจริง และข้อแลกเปลี่ยนด้านประสิทธิภาพเมื่อเทียบกับการดำเนินการ JOIN

  • 🔍 คำจำกัดความหลัก: ซับเควรีคือคำสั่ง SELECT ที่ซ้อนอยู่ภายในเควรีอื่น โดยเควรีภายในจะทำงานก่อนเพื่อส่งค่าให้กับเควรีภายนอก
  • 🧮 ซับเควรีแบบสเกลาร์: ฟังก์ชันนี้จะส่งคืนข้อมูลเพียงแถวเดียวและคอลัมน์เดียว ดังนั้นจึงใช้คู่กับตัวดำเนินการเปรียบเทียบ เช่น เท่ากับ มากกว่า หรือ น้อยกว่า
  • 📋 แบบสอบถามย่อยสำหรับแถวและตาราง: การค้นหาแบบย่อยตามแถวจะส่งคืนแถวเดียวที่มีหลายคอลัมน์ ในขณะที่การค้นหาแบบย่อยตามตารางจะส่งคืนหลายแถวและทำงานร่วมกับตัวดำเนินการ IN
  • 🧩 ความลึกของการซ้อน: สามารถใช้ซับเควรีแบบซ้อนกันได้หลายระดับ ซึ่งจะช่วยค้นหาค่าต่างๆ เช่น สมาชิกที่จ่ายเงินสูงสุดได้ในคำสั่งเดียว
  • 🇧🇷 นอกเหนือจาก SELECT: คำสั่ง INSERT, UPDATE และ DELETE รองรับซับเควรี ทำให้สามารถเปลี่ยนแปลงข้อมูลจำนวนมากได้โดยไม่ต้องใช้ตารางชั่วคราว
  • กฎการประเมินผล: โดยปกติแล้ว คำสั่ง JOIN จะทำงานเร็วกว่าคำสั่งย่อย (subquery) ที่เทียบเท่ากันมาก ดังนั้นควรสงวนคำสั่งย่อยไว้สำหรับตรรกะที่คำสั่ง JOIN ไม่สามารถแสดงได้

MySQL แบบสอบถามย่อย

SubQuery ใน SQL คืออะไร?

A แบบสอบถามย่อย SELECT คือคำสั่ง SELECT ที่อยู่ภายในคำสั่ง SELECT อื่น โดยปกติแล้ว คำสั่ง SELECT ภายในจะใช้เพื่อกำหนดผลลัพธ์ของคำสั่ง SELECT ภายนอก ดังนั้นฐานข้อมูลจะประเมินคำสั่งภายในก่อน แล้วจึงส่งผลลัพธ์ขึ้นไปด้านบน

แบบสอบถามภายในเรียกว่า แบบสอบถามภายใน หรือแบบสอบถามแบบซ้อนกัน และคำสั่งที่บรรจุแบบสอบถามนั้นเรียกว่า การสอบถามภายนอกเรามาดูไวยากรณ์ของซับเควรีกัน

MySQL แบบสอบถามย่อย

แผนภาพด้านบนแสดงโครงสร้างทั่วไปของคำสั่ง: คำสั่ง SELECT ภายนอกระบุคอลัมน์ที่คุณต้องการดู และคำสั่ง SELECT ภายในที่อยู่ในวงเล็บระบุค่าหรือรายการค่าที่ใช้ในการเปรียบเทียบในส่วน WHERE

เหตุใดจึงต้องใช้ซับเควรี?

ก่อนที่จะไปดูประเภทต่างๆ เราควรทำความเข้าใจก่อนว่าเมื่อใดที่ซับเควรีเหมาะสมที่จะใช้ในคำสั่ง SQL

ซับเควรีใช้ตอบคำถามที่ค่าตัวกรองไม่เป็นที่ทราบล่วงหน้า จะต้องคำนวณค่าตัวกรองจากข้อมูลเอง ตัวอย่างเช่น ข้อร้องเรียนทั่วไปของลูกค้าที่ร้านเช่าภาพยนตร์ MyFlix คือจำนวนภาพยนตร์น้อย และฝ่ายบริหารต้องการซื้อภาพยนตร์ในหมวดหมู่ที่มีจำนวนเรื่องน้อยที่สุด ไม่มีใครรู้ว่าหมวดหมู่นั้นคือหมวดหมู่ใดจนกว่าจะสอบถามฐานข้อมูล ดังนั้นจึงต้องคำนวณค่าตัวกรองก่อนแล้วจึงนำมาใช้เป็นตัวกรอง

การค้นหาย่อยอยู่ที่tracมีเหตุผลเชิงปฏิบัติสามประการที่ทำให้ควรเลือกใช้:

  • การอ่าน: แต่ละส่วนของตรรกะจะอยู่ในวงเล็บแยกกัน ทำให้ประโยคนั้นอ่านได้เหมือนเป็นลำดับของคำถามเล็กๆ แทนที่จะเป็นนิพจน์ที่ซับซ้อนเพียงอย่างเดียว
  • การแยก: สามารถเรียกใช้คิวรีภายในได้โดยอิสระเพื่อยืนยันว่าได้ค่าที่คาดหวังไว้ ซึ่งทำให้การทดสอบและการแก้ไขข้อผิดพลาดง่ายขึ้นมาก
  • ความยืดหยุ่น: รูปแบบเดียวกันนี้ใช้ได้กับเงื่อนไข WHERE, HAVING, SELECT และ FROM รวมถึงภายในคำสั่ง INSERT, UPDATE และ DELETE ด้วย

ข้อแลกเปลี่ยนคือความเร็ว ซึ่งจะได้รับการตรวจสอบในการเปรียบเทียบ JOIN ในภายหลังของบทความนี้

ประเภทของซับเควรีใน MySQL

MySQL รองรับการใช้ซับเควรี 3 ประเภท โดยประเภทจะขึ้นอยู่กับรูปแบบของผลลัพธ์ที่เควรีภายในส่งคืน แต่ละประเภทจะอธิบายไว้ด้านล่างพร้อมตัวอย่างการใช้งานกับฐานข้อมูล myflixdb

1) แบบสอบถามย่อยแบบสเกลาร์

A แบบสอบถามย่อยแบบสเกลาร์ คำสั่งนี้จะส่งคืนแถวเดียวและคอลัมน์เดียว ซึ่งหมายความว่ามันจะส่งคืนค่าเดียว เนื่องจากผลลัพธ์เป็นค่าเดียว จึงสามารถใช้ได้ทุกที่ที่อนุญาตให้ใช้ค่าคงที่ได้ กลับมาที่ปัญหา MyFlix ข้างต้น คุณสามารถใช้คำสั่งแบบนี้ได้:

SELECT category_name FROM categories
WHERE category_id = (SELECT MIN(category_id) FROM movies);

ผลลัพธ์ที่ได้คือ:

MySQL แบบสอบถามย่อย

มาดูกันว่าคำสั่งนี้ทำงานอย่างไร

MySQL แบบสอบถามย่อย

ดังที่แผนภาพการดำเนินการแสดงไว้ MySQL วิ่งครั้งแรก SELECT MIN(category_id) FROM moviesรับค่าเพียงค่าเดียว จากนั้นจึงเรียกใช้คำสั่งค้นหาภายนอกโดยใช้ค่านั้น เนื่องจากส่งค่ากลับมาเพียงค่าเดียว ตัวดำเนินการที่อนุญาตจึงเป็นชุดการเปรียบเทียบมาตรฐาน: =, <> (หรือ !=), >, >=, <และ <=.

💡 เคล็ดลับ: หากวางซับเควรีไว้หลังจากนั้น = ส่งคืนมากกว่าหนึ่งแถว MySQL ทำให้เกิดข้อผิดพลาด 1242 แบบสอบถามย่อยส่งคืนข้อมูลมากกว่า 1 แถวเปลี่ยนตัวดำเนินการเป็น INหรือกระชับเงื่อนไข WHERE ภายในให้แน่นขึ้น

2) การค้นหาย่อยแถว

A แบบสอบถามย่อยแถว นอกจากนี้ยังส่งคืนแถวเดียว แต่แถวนั้นอาจมีมากกว่าหนึ่งคอลัมน์ ดังนั้นแบบสอบถามภายนอกจึงเปรียบเทียบค่าในแถวกับตัวสร้างแถวแทนที่จะเปรียบเทียบกับค่าเดียว

SELECT full_names, contact_number FROM members
WHERE (membership_number, gender) = (SELECT membership_number, gender FROM members WHERE full_names = 'Janet Jones');

ตัวดำเนินการที่อนุญาตให้ใช้ได้คือตัวดำเนินการเปรียบเทียบแบบเดียวกันกับที่ระบุไว้ข้างต้น โดยนำไปใช้กับทั้งแถวพร้อมกัน

3) แบบสอบถามย่อยของตาราง

A ตารางย่อย คำสั่งค้นหาภายนอกจะส่งคืนหลายแถว และมักจะมีหลายคอลัมน์ ดังนั้นคำสั่งค้นหาภายนอกจึงต้องใช้ตัวดำเนินการเซต เช่น IN, NOT IN, ANY, ALLหรือ EXISTS.

สมมติว่าคุณต้องการชื่อและเบอร์โทรศัพท์ของสมาชิกที่เช่าภาพยนตร์ไปแล้วแต่ยังไม่ส่งคืน เพื่อที่คุณจะได้โทรไปเตือน คุณสามารถใช้คำสั่งค้นหาแบบนี้ได้:

SELECT full_names, contact_number FROM members
WHERE membership_number IN (SELECT membership_number FROM movierentals WHERE return_date IS NULL);

MySQL แบบสอบถามย่อย

มาดูกันว่าคำสั่งนี้ทำงานอย่างไร

MySQL แบบสอบถามย่อย

ในกรณีนี้ การค้นหาภายในให้ผลลัพธ์มากกว่าหนึ่งรายการ ดังนั้นรายการหมายเลขสมาชิกจึงถูกส่งต่อไปยัง IN ตัวดำเนินการและสมาชิกที่ตรงกันทั้งหมดจะถูกส่งคืน

การซ้อนแบบสอบถามย่อยหลายระดับลึก

จนถึงตอนนี้คุณได้เห็นไปแล้วสองระดับ ซับเควรีอาจประกอบด้วยซับเควรีอื่นอีก ซึ่งจะทำให้เกิดคำสั่งซ้อนกันสามชั้น

สมมติว่าฝ่ายบริหารต้องการให้รางวัลแก่สมาชิกที่จ่ายเงินสูงสุด เราสามารถเรียกใช้คำสั่งค้นหาแบบนี้ได้:

SELECT full_names FROM members
WHERE membership_number = (SELECT membership_number FROM payments
    WHERE amount_paid = (SELECT MAX(amount_paid) FROM payments));

คำสั่งค้นหาภายในสุดจะค้นหาจำนวนเงินที่มากที่สุด คำสั่งค้นหาตรงกลางจะแปลงจำนวนเงินนั้นเป็นหมายเลขสมาชิก และคำสั่งค้นหาภายนอกสุดจะแปลงหมายเลขสมาชิกเป็นชื่อ คำสั่งค้นหาข้างต้นให้ผลลัพธ์ดังต่อไปนี้:

MySQL แบบสอบถามย่อย

วิธีการใช้ซับเควรีร่วมกับคำสั่ง INSERT, UPDATE และ DELETE

การใช้ซับเควรีไม่ได้จำกัดอยู่แค่ในคำสั่ง SELECT เท่านั้น รูปแบบวงเล็บเดียวกันนี้สามารถใช้ได้ภายในคำสั่งแก้ไขข้อมูล ซึ่งช่วยให้สามารถเปลี่ยนแปลงข้อมูลทั้งแถวได้ในครั้งเดียวโดยไม่ต้องสร้างตารางชั่วคราว

แทรกข้อมูลโดยใช้ซับเควรี ซับเควรีสามารถให้ข้อมูลแถวที่จะถูกแทรก ซึ่งเป็นการคัดลอกข้อมูลจากตารางหนึ่งไปยังอีกตารางหนึ่ง รายชื่อคอลัมน์ของคำสั่ง SELECT ต้องตรงกับรายชื่อคอลัมน์ของคำสั่ง INSERT

INSERT INTO vip_members (membership_number, full_names)
SELECT membership_number, full_names FROM members
WHERE membership_number IN (SELECT membership_number FROM payments WHERE amount_paid > 5000);

อัปเดตด้วยซับเควรี ในที่นี้ คำสั่งค้นหาภายในจะเป็นตัวกำหนดว่าแถวใดบ้างที่จะถูกเรียกใช้ ตัวอย่างด้านล่างจะทำเครื่องหมายสมาชิกทุกคนที่ยังค้างชำระค่าเช่าอยู่

UPDATE members
SET reminder_sent = 1
WHERE membership_number IN (SELECT membership_number FROM movierentals WHERE return_date IS NULL);

ลบข้อมูลโดยใช้ซับเควรี หลักการเดียวกันนี้ใช้ในการลบแถวที่ตรงตามเงื่อนไขที่กำหนดไว้ในตารางที่สอง

DELETE FROM members
WHERE membership_number NOT IN (SELECT membership_number FROM movierentals);

⚠️คำเตือน: MySQL ไม่อนุญาตให้คำสั่งแก้ไขตารางและเลือกข้อมูลจากตารางเดียวกันภายในซับควอรีในส่วน FROM หากเกิดข้อผิดพลาด 1093 ให้ห่อซับควอรีภายในด้วยตารางที่ได้มา เช่น SELECT * FROM (SELECT ...) AS tเพื่อให้ MySQL แสดงผลลัพธ์ก่อนที่จะมีการเปลี่ยนแปลง นอกจากนี้ ควรเรียกใช้คำสั่ง SELECT ภายในแยกต่างหากก่อน และตรวจสอบจำนวนแถวก่อนที่จะเรียกใช้คำสั่ง SELECT อีกครั้ง อัพเดท หรือ ลบ ในการผลิต

ซับเควรี vs การเชื่อมตาราง

ทั้งซับเควรีและ JOIN สามารถรวมข้อมูลจากหลายตารางได้ ดังนั้นคำถามที่ตามมาก็คือควรเลือกใช้แบบใด

เมื่อเปรียบเทียบกับการเชื่อมตาราง (joins) แล้ว ซับควอรี (sub-queries) นั้นใช้งานง่ายและอ่านง่ายกว่า ไม่ซับซ้อนเท่ากับการเชื่อมตาราง (joins) ร่วมและด้วยเหตุนี้จึงมักถูกนำไปใช้โดย ผู้เริ่มต้น SQL.

แต่การใช้ซับเควรีมีปัญหาเรื่องประสิทธิภาพ การใช้ join แทนซับเควรีบางครั้งอาจเพิ่มประสิทธิภาพได้มากถึง 500 เท่า เพราะตัวปรับแต่งประสิทธิภาพสามารถประมวลผล join ได้ในครั้งเดียว แทนที่จะประเมินคำสั่งภายในซ้ำๆ

จุดเปรียบเทียบ แบบสอบถามย่อย สมัคร
การอ่าน สูง เนื่องจากแต่ละบล็อกตอบคำถามหนึ่งข้อ ต่ำกว่า เนื่องจากตารางทั้งหมดปรากฏอยู่ในประโยคเดียว
ประสิทธิภาพ การทำงานจะช้าลง เนื่องจากคิวรีภายในอาจทำงานสำหรับทุกแถวภายนอก เร็วกว่า โดยมักจะเร็วกว่ามาก
คอลัมน์ผลลัพธ์ เฉพาะคอลัมน์ของตารางภายนอกเท่านั้นที่จะถูกส่งคืน สามารถส่งคืนคอลัมน์จากทุกตารางที่เชื่อมต่อได้
การใช้งานทั่วไป การกรองตามค่าที่ต้องคำนวณก่อน การรวมแถวที่เกี่ยวข้องจากสองตารางขึ้นไป
โค้งการเรียนรู้ อ่อนโยน คุ้นเคยสำหรับผู้เริ่มต้น ความชันที่สูงขึ้น จำเป็นต้องมีความรู้เกี่ยวกับประเภทของการเชื่อมต่อ

เมื่อเลือกตัวเลือกแล้ว ขอแนะนำให้ใช้ JOIN เหนือแบบสอบถามย่อย ควรใช้ซับเควรีเป็นทางเลือกสำรองเท่านั้น ในกรณีที่คุณไม่สามารถใช้การดำเนินการ JOIN เพื่อให้ได้ผลลัพธ์ข้างต้นได้

แบบสอบถามย่อยเทียบกับการรวม

นอกจากนี้ ซับเควรียังสามารถแยกย่อยออกเป็นส่วนประกอบเชิงตรรกะได้ง่าย ซึ่งมีประโยชน์มากเมื่อ การทดสอบ และแก้ไขคำถาม

คำถามที่พบบ่อย

ซับเควรีแบบสัมพันธ์ (Correlated subquery) จะอ้างอิงถึงคอลัมน์ของเควรีภายนอก ดังนั้นจึงมีการประเมินค่าหนึ่งครั้งสำหรับทุกแถวในเควรีภายนอก ในขณะที่ซับเควรีแบบไม่สัมพันธ์ (Non-correlated subquery) จะเป็นอิสระและทำงานเพียงครั้งเดียว ซับเควรีแบบสัมพันธ์มีประสิทธิภาพสูง แต่จะทำงานช้าลงอย่างเห็นได้ชัดในตารางขนาดใหญ่

ซับเควรีสามารถอยู่ในส่วน WHERE, ส่วน HAVING, รายการ SELECT หรือส่วน FROM ได้ โดยจะกลายเป็นตารางที่ได้มาและต้องมีชื่อเรียกแทน นอกจากนี้ยังสามารถใช้ได้ภายในคำสั่ง INSERT, UPDATE และ DELETE ด้วย

บ่อยครั้งที่เป็นเช่นนั้น เช่น ผู้ช่วย AI ที่ติดตั้งมาในโปรแกรมตัดต่อวิดีโอ MySQL ม้านั่งทำงานของช่างเครื่อง สามารถเสนอสิ่งที่เทียบเท่าได้ สมัครควรเปรียบเทียบจำนวนแถวและอ่านแผนการทำงานของ EXPLAIN ก่อนที่จะเชื่อถือการเขียนใหม่เสมอ เนื่องจากวิธีการจัดการค่า NULL อาจแตกต่างกัน

ใช่แล้ว โปรแกรมแปลงข้อความเป็น SQL จะแปลงคำถาม เช่น “หมวดหมู่ใดมีภาพยนตร์น้อยที่สุด” ให้เป็นคำสั่ง SELECT แบบซ้อนกัน ความถูกต้องขึ้นอยู่กับโครงสร้างข้อมูลที่ป้อนให้กับโมเดล ดังนั้นควรตรวจสอบคำสั่งที่สร้างขึ้นเทียบกับชื่อตารางจริง

ข้อผิดพลาดนี้เกิดขึ้นเมื่อซับเควรีที่วางอยู่หลังตัวดำเนินการเปรียบเทียบส่งคืนหลายแถว ให้แทนที่ตัวดำเนินการด้วย IN, ANY หรือ EXISTS หรือปรับปรุงเงื่อนไข WHERE ภายในให้กระชับขึ้นเพื่อให้ส่งคืนเพียงแถวเดียว

สรุปโพสต์นี้ด้วย: