🗄️ MySQL/MariaDB

เจาะลึก MySQL และ MariaDB Subqueries: วิธีเขียน Query ซ้อน Query ให้โปรแกรมทำงานฉลาดขึ้น

10 นาที 16 views บันทึกเป็น PDF
เจาะลึก MySQL และ MariaDB Subqueries: วิธีเขียน Query ซ้อน Query ให้โปรแกรมทำงานฉลาดขึ้น

มือใหม่หัดเขียน SQL ต้องรู้! เรียนรู้วิธีใช้ Subquery (การเขียนคำสั่งถามข้อมูลซ้อนกัน) ใน WHERE, SELECT และ FROM เพื่อดึงข้อมูลซับซ้อนได้แม่นยำและสะอาดตา

Subquery คืออะไรและทำไมเราต้องใช้มัน

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

-- หาชื่อพนักงานที่ได้รับเงินเดือนสูงกว่าค่าเฉลี่ยของบริษัท
SELECT name 
FROM employees 
WHERE salary > (SELECT AVG(salary) FROM employees); -- นี่คือ Subquery

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

การวาง Subquery ในส่วน WHERE และ SELECT

ตำแหน่งที่ใช้ Subquery บ่อยที่สุดคือในส่วนของ WHERE (เงื่อนไขในการเลือกข้อมูล) เพื่อกรองข้อมูลตามค่าที่เราคำนวณได้ใหม่ นอกจากนี้เรายังสามารถวางไว้ใน SELECT เพื่อให้มันแสดงผลเป็นคอลัมน์ใหม่ที่คำนวณมาจากข้อมูลตารางอื่นได้ด้วยครับ เปรียบได้กับการที่คุณทำตารางสรุปรายได้ แล้วอยากได้คอลัมน์เพิ่มมาบอกว่า "รายได้นี้ คิดเป็นกี่เปอร์เซ็นต์ของรายได้รวมทั้งเดือน" การใส่ไว้ใน WHERE ช่วยเรื่องการคัดกรองข้อมูลที่ซับซ้อน เช่น การหาลูกค้าที่เคยสั่งซื้อสินค้าเกิน 5 ครั้ง โดยเราไม่ต้องรู้จำนวนที่แน่นอนล่วงหน้า แต่ให้ฐานข้อมูลไปนับมาให้เอง ส่วนการใส่ใน SELECT จะมีประโยชน์มากเวลาทำรายงานสรุปข้อมูล เพราะมันจะแสดงค่าคำนวณข้างๆ ข้อมูลหลักได้ทันทีโดยไม่ต้องไปวนลูปเขียนโปรแกรมเพิ่ม จุดที่ต้องระวังคือเรื่อง Performance (ประสิทธิภาพการทำงานของโค้ด) เพราะถ้าคุณใช้ Subquery ใน SELECT กับข้อมูลจำนวนมหาศาล ฐานข้อมูลจะต้องทำงานคำนวณใหม่ในทุกๆ แถวที่แสดงผล ซึ่งอาจทำให้โปรแกรมของคุณทำงานช้าลงอย่างเห็นได้ชัดครับ มือใหม่ควรเริ่มฝึกจากข้อมูลชุดเล็กๆ ก่อน เพื่อดูว่าผลลัพธ์ที่ได้ถูกต้องตรงตามความต้องการหรือไม่

-- แสดงชื่อสินค้า พร้อมจำนวนยอดขายรวมของสินค้านั้น
SELECT product_name, 
       (SELECT COUNT(*) FROM orders WHERE orders.product_id = products.id) as total_orders
FROM products;

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

การใช้ Subquery ในส่วน FROM เพื่อสร้างตารางชั่วคราว

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

-- หาค่าเฉลี่ยยอดขายรายวัน แล้วดึงเฉพาะวันที่ขายได้มากกว่าค่าเฉลี่ย
SELECT * 
FROM (SELECT date, SUM(amount) as daily_total FROM sales GROUP BY date) as daily_sales
WHERE daily_total > 5000;

ในตัวอย่างนี้ เราสร้างตารางชื่อ daily_sales ขึ้นมาจากการรวมยอดขายรายวันก่อน แล้วค่อยสั่ง WHERE เพื่อคัดกรองเฉพาะวันที่ยอดขายมากกว่า 5,000 ออกมาแสดง ผลลัพธ์ที่ได้คือรายชื่อวันที่ทำยอดขายได้ตามเป้าครับ

คำสั่ง EXISTS และ NOT EXISTS เพื่อตรวจสอบการมีอยู่

นอกจาก Subquery ปกติแล้ว เรายังมีคำสั่งพิเศษที่ชื่อว่า EXISTS (การตรวจสอบว่ามีข้อมูลอยู่จริงไหม) ซึ่งใช้สำหรับเช็คว่าในตารางเป้าหมายมีข้อมูลที่ตรงกับเงื่อนไขของเราหรือเปล่าครับ คำสั่งนี้จะคืนค่าเป็น "จริง" หรือ "เท็จ" เท่านั้น ไม่ได้ดึงตัวข้อมูลออกมาตรงๆ เหมือน SELECT ทั่วไป ลองจินตนาการว่าคุณเป็นพนักงานตรวจบัตรหน้างาน คุณแค่ต้องการรู้ว่า "คนนี้มีรายชื่อในลิสต์หรือไม่" ไม่จำเป็นต้องขอดูที่อยู่หรือเบอร์โทรศัพท์ของเขา EXISTS ทำหน้าที่แบบเดียวกันครับ มันแค่เช็คว่า "มี" หรือ "ไม่มี" ซึ่งในหลายกรณีมันทำงานได้เร็วกว่าการไปดึงข้อมูลมาเปรียบเทียบกันตรงๆ ส่วน NOT EXISTS ก็คือการทำงานแบบตรงกันข้าม คือใช้เช็คว่า "ไม่มีข้อมูลนี้อยู่" เช่น การหาลูกค้าที่ไม่เคยสั่งซื้อสินค้าเลยแม้แต่ครั้งเดียว การใช้คำสั่งเหล่านี้จะทำให้โค้ดของคุณดูสะอาดและอ่านง่ายกว่าการใช้ JOIN ในบางสถานการณ์ที่ต้องการแค่การเช็คสถานะครับ

-- ค้นหาลูกค้าที่เคยสั่งซื้อสินค้าอย่างน้อยหนึ่งครั้ง
SELECT name 
FROM customers c 
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

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

ข้อควรระวังและแนวปฏิบัติที่ดีสำหรับมือใหม่

เวลาที่คุณเริ่มใช้ Subquery บ่อยขึ้น สิ่งที่มักจะเจอก็คือโค้ดเริ่มอ่านยากและหาจุดผิดลำบากครับ กฎเหล็กของโปรแกรมเมอร์คือ "เขียนโค้ดให้คนอ่านออก" ดังนั้นควรจัดรูปแบบโค้ดให้สวยงามด้วยการขึ้นบรรทัดใหม่และย่อหน้าเสมอ อย่าเขียนทุกอย่างรวมกันเป็นบรรทัดเดียวเด็ดขาด อีกเรื่องคือเรื่อง Index (ดัชนีช่วยค้นหาข้อมูล) ครับ ถ้า Subquery ของคุณต้องทำงานกับข้อมูลหลักล้านแถว แต่ไม่มีการทำ Index ที่คอลัมน์ที่ใช้เปรียบเทียบ โปรแกรมของคุณจะทำงานช้าเหมือนเต่าคลาน การหมั่นตรวจสอบด้วยคำสั่ง EXPLAIN (คำสั่งดูขั้นตอนการทำงานของฐานข้อมูล) จะช่วยให้คุณเห็นว่าฐานข้อมูลทำงานอย่างไรและต้องปรับปรุงตรงไหน สุดท้ายอย่าลืมว่า Subquery ไม่ใช่คำตอบของทุกอย่างครับ บางครั้งการใช้ JOIN อาจจะเหมาะสมและเร็วกว่า ให้ลองเปรียบเทียบผลลัพธ์และเวลาที่ใช้ในการรันคำสั่งดูบ่อยๆ แล้วคุณจะเริ่มมีเซนส์เองว่าเมื่อไหร่ควรใช้แบบไหนครับ

  1. จัดรูปแบบโค้ดให้เป็นสัดส่วนเสมอ เพื่อให้เพื่อนร่วมทีมอ่านเข้าใจง่าย
  2. ใช้คำสั่ง EXPLAIN นำหน้า Query เพื่อดูว่าฐานข้อมูลทำงานหนักเกินไปไหม
  3. ลองเปรียบเทียบการใช้ JOIN กับ Subquery ในงานเดียวกัน เพื่อเรียนรู้ความแตกต่าง

สรุป: การประยุกต์ใช้ในการทำงานจริง

การเขียน Subquery และการใช้ EXISTS เป็นทักษะพื้นฐานที่จะทำให้คุณข้ามจากระดับมือใหม่ไปสู่การจัดการข้อมูลที่ซับซ้อนขึ้นได้ครับ ในชีวิตการทำงานจริง คุณมักจะเจอโจทย์อย่าง "ดึงรายชื่อพนักงานที่ทำยอดขายสูงที่สุดในแต่ละแผนก" หรือ "หาลูกค้าที่ยังไม่ได้จ่ายเงินในเดือนที่ผ่านมา" ซึ่งทั้งหมดนี้สามารถแก้ได้ด้วยเทคนิคที่เราคุยกันมาครับ การฝึกฝนที่ดีที่สุดคือการสร้างโปรเจกต์จำลอง เช่น โปรเจกต์ระบบร้านค้าออนไลน์ หรือระบบจัดการคลังสินค้า แล้วลองเขียน Query เพื่อหาข้อมูลสรุปต่างๆ เหล่านี้ดูครับ ยิ่งคุณเจอบั๊กหรือเจอข้อผิดพลาดมากเท่าไหร่ คุณก็จะยิ่งเก่งขึ้นเท่านั้น อย่ากลัวที่จะลองผิดลองถูกเพราะนั่นคือวิธีที่โปรแกรมเมอร์ทุกคนต้องผ่าน จำไว้ว่าความเก่งไม่ได้วัดกันที่ว่าคุณเขียนโค้ดซับซ้อนแค่ไหน แต่วัดกันที่ว่าคุณเขียนโค้ดที่ทำงานได้ถูกต้องและคนอื่นอ่านรู้เรื่องหรือไม่ เริ่มจากสิ่งเล็กๆ วันละนิด แล้วคุณจะพบว่าการจัดการฐานข้อมูลด้วย Subquery นั้นสนุกและมีประโยชน์มหาศาลในการทำงานสายนี้ครับ

แชร์บทความ

Facebook X LINE

บทความที่เกี่ยวข้อง

MySQL/MariaDB

สอนวิธี Backup และ Restore ฐานข้อมูล MySQL/MariaDB ด้วย mysqldump บน Windows

มือใหม่หัดเขียนโปรแกรมต้องรู้! วิธีสำรองข้อมูล (Backup) และกู้คืน (Restore) ฐานข้อมูลด้วย mysqldump ผ่าน Command Line ป้องกันงานหาย ทำตามได้จริง

1 month ago 9 นาที
24 views
จัดการ User และสิทธิ์ใน MySQL/MariaDB ให้ปลอดภัยด้วยคำสั่ง GRANT และ REVOKE
MySQL/MariaDB

จัดการ User และสิทธิ์ใน MySQL/MariaDB ให้ปลอดภัยด้วยคำสั่ง GRANT และ REVOKE

มือใหม่หัดใช้ฐานข้อมูลต้องรู้! วิธีสร้าง User, กำหนดสิทธิ์ (Permission) และตั้งค่าความปลอดภัยให้ MySQL/MariaDB เพื่อป้องกันข้อมูลรั่วไหลแบบมือโปร

1 month ago 8 นาที
27 views
เข้าใจ MySQL และ MariaDB Trigger: เขียนคำสั่งอัตโนมัติให้ฐานข้อมูลทำงานแทนเรา
MySQL/MariaDB

เข้าใจ MySQL และ MariaDB Trigger: เขียนคำสั่งอัตโนมัติให้ฐานข้อมูลทำงานแทนเรา

อยากให้ฐานข้อมูลทำงานอัตโนมัติเมื่อมีการเพิ่มหรือลบข้อมูลไหม? มาเรียนรู้การใช้ Trigger (ตัวกระตุ้นการทำงาน) ใน MySQL และ MariaDB เพื่อลดภาระงานโค้ดกัน

1 month ago 8 นาที
24 views