MS-SQL

เจาะลึก MS-SQL Subqueries และการใช้งาน CROSS APPLY สำหรับมือใหม่

8 นาที 15 views บันทึกเป็น PDF
เจาะลึก MS-SQL Subqueries และการใช้งาน CROSS APPLY สำหรับมือใหม่

เรียนรู้วิธีใช้ Subquery เขียนคำสั่งซ้อนเพื่อกรองข้อมูล และทำความรู้จัก CROSS APPLY ตัวช่วยจัดการ Table-Valued Functions ใน MS-SQL ให้ชีวิตโปรแกรมเมอร์ง่ายขึ้น

ทำความรู้จัก Subquery การเขียนคำสั่งซ้อนในคำสั่ง

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

-- ค้นหาพนักงานที่มีเงินเดือนมากกว่าเงินเดือนเฉลี่ยของบริษัท
SELECT Name, Salary
FROM Employees
WHERE Salary > (SELECT AVG(Salary) FROM Employees);

อธิบายโค้ด:

  • SELECT AVG(Salary) FROM Employees คือส่วนที่ซ้อนอยู่ข้างใน เพื่อหาค่าเฉลี่ยเงินเดือนของทุกคนก่อน
  • WHERE Salary > (...) คือคำสั่งหลักที่เอาค่าที่ได้จากข้างในมาเปรียบเทียบกับเงินเดือนของพนักงานแต่ละคน

ผลลัพธ์ที่ได้: รายชื่อพนักงานและเงินเดือนของคนที่ได้รับเงินมากกว่าค่าเฉลี่ยของบริษัททุกคนออกมาเป็นตาราง

ข้อจำกัดของ Subquery และจุดที่ต้องระวัง

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

รู้จัก Table-Valued Function ตัวช่วยสร้างตารางจำลอง

ก่อนจะไปถึง CROSS APPLY เราต้องรู้จัก Table-Valued Function (ฟังก์ชันที่ทำงานแล้วคืนค่าออกมาเป็นรูปแบบตาราง) กันก่อน ปกติฟังก์ชันทั่วไปจะคืนค่าออกมาแค่ค่าเดียว เช่น ตัวเลขหรือข้อความ แต่ฟังก์ชันประเภทนี้จะให้ผลลัพธ์เป็นชุดข้อมูลเหมือนตารางจริง ลองคิดว่ามันคือ "แม่พิมพ์ทำขนม" ที่เราใส่ส่วนผสม (ข้อมูล) ลงไป แล้วมันจะผลิตขนมออกมาเป็นชุด (ตาราง) ให้เราเสมอ ฟังก์ชันนี้มีประโยชน์มากเวลาเราต้องการคำนวณข้อมูลที่ซับซ้อนแยกต่างหาก แล้วค่อยเอาผลลัพธ์นั้นมาเชื่อมกับข้อมูลหลักของเรา ถ้าไม่มี Table-Valued Function เราอาจต้องเขียนโค้ดชุดเดิมซ้ำๆ หลายที่ แต่ถ้าเราสร้างมันขึ้นมาครั้งเดียว เราก็เรียกใช้ได้ตลอด ช่วยลดการเขียนโค้ดที่ซ้ำซ้อนและทำให้การบำรุงรักษาโค้ดในระยะยาวทำได้ง่ายขึ้นมาก

-- สร้างฟังก์ชันหาพนักงานในแผนกที่ระบุ
CREATE FUNCTION GetEmployeesByDept (@DeptID INT)
RETURNS TABLE
AS
RETURN (
    SELECT Name, Salary FROM Employees WHERE DepartmentID = @DeptID
);

อธิบายโค้ด:

  • CREATE FUNCTION คือคำสั่งสร้างฟังก์ชัน
  • @DeptID INT คือตัวแปรที่รับค่ารหัสแผนกเข้ามา
  • RETURNS TABLE บอกว่าฟังก์ชันนี้จะส่งผลลัพธ์ออกมาเป็นตาราง

ผลลัพธ์ที่ได้: เราจะได้ฟังก์ชันชื่อ GetEmployeesByDept ที่พร้อมใช้งานเหมือนตารางตัวหนึ่ง

ความมหัศจรรย์ของ CROSS APPLY

CROSS APPLY คือสิ่งที่ถูกสร้างมาเพื่อ MS-SQL โดยเฉพาะ มันทำหน้าที่เหมือนการนำตารางหลักไปเชื่อมกับ Table-Valued Function โดยที่ฟังก์ชันนั้นสามารถดึงค่าจากตารางหลักมาใช้เป็นพารามิเตอร์ (ตัวแปรขาเข้า) ได้แบบแถวต่อแถว เปรียบเทียบง่ายๆ เหมือนคุณมีรายชื่อพนักงาน แล้วคุณมีเครื่องมือวิเคราะห์ (ฟังก์ชัน) ที่ต้องใช้ "ชื่อพนักงาน" เป็นตัวตั้งต้น CROSS APPLY จะหยิบพนักงานคนแรกไปใส่เครื่องมือ แล้วเอาผลลัพธ์มาวางคู่กัน ทำแบบนี้ไปเรื่อยๆ จนครบทุกคน ถ้าใช้ JOIN (คำสั่งเชื่อมตาราง) ปกติ เราไม่สามารถส่งค่าจากตารางหลักเข้าไปในฟังก์ชันได้โดยตรง แต่ CROSS APPLY ออกแบบมาเพื่อแก้ปัญหานี้โดยเฉพาะ ทำให้การเชื่อมข้อมูลที่ต้องการการประมวลผลพิเศษกลายเป็นเรื่องง่าย

-- เชื่อมข้อมูลพนักงานกับฟังก์ชันหาเงินเดือนย้อนหลัง 3 เดือน
SELECT E.Name, D.Salary
FROM Employees AS E
CROSS APPLY GetLastThreeMonthsSalary(E.EmployeeID) AS D;

อธิบายโค้ด:

  • CROSS APPLY จะวิ่งไปหาพนักงานทีละคน
  • GetLastThreeMonthsSalary(E.EmployeeID) จะส่งรหัสพนักงานคนนั้นเข้าฟังก์ชัน
  • AS D คือการตั้งชื่อเล่นให้ผลลัพธ์ที่ได้จากฟังก์ชัน

ผลลัพธ์ที่ได้: ตารางรายชื่อพนักงานที่แสดงเงินเดือนย้อนหลัง 3 เดือนของแต่ละคนเรียงต่อกัน

ความต่างระหว่าง CROSS APPLY และ OUTER APPLY

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

-- ใช้ OUTER APPLY เพื่อให้พนักงานที่ไม่มีประวัติเงินเดือนยังปรากฏอยู่
SELECT E.Name, D.Salary
FROM Employees AS E
OUTER APPLY GetLastThreeMonthsSalary(E.EmployeeID) AS D;

อธิบายโค้ด:

  • OUTER APPLY ทำงานเหมือน CROSS APPLY แต่ไม่ตัดแถวที่ฟังก์ชันคืนค่าว่าง
  • ถ้าไม่มีข้อมูลเงินเดือน D.Salary จะแสดงเป็นค่าว่างแต่ชื่อพนักงานยังอยู่

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

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

การเข้าใจ Subquery และกลุ่มคำสั่ง APPLY จะช่วยให้คุณจัดการข้อมูลที่ซับซ้อนในฐานข้อมูลได้อย่างมืออาชีพ ไม่ว่าจะเป็นการดึงข้อมูลสถิติรายบุคคล หรือการเชื่อมต่อกับฟังก์ชันที่ต้องคำนวณค่าเฉพาะเจาะจง ลองนึกภาพโปรเจกต์ทำระบบรายงานเงินเดือน หากคุณต้องดึงข้อมูลพนักงานพร้อมกับยอดรวมโบนัสที่ต้องคำนวณผ่านสูตรพิเศษ ฟังก์ชัน Table-Valued ร่วมกับ OUTER APPLY จะเป็นเครื่องมือที่ทรงพลังที่สุดในการดึงข้อมูลนี้ออกมาในคราวเดียวโดยไม่ต้องเขียนโค้ดซ้ำซ้อน จำไว้ว่าพื้นฐานที่ดีคือการฝึกฝน เริ่มจากการเขียน Subquery ง่ายๆ ก่อน แล้วค่อยขยับมาใช้ APPLY เมื่อต้องเจอกับงานที่ยากขึ้น หมั่นทดลองรันโค้ดบ่อยๆ แล้วคุณจะเริ่มเห็นความสวยงามของการเชื่อมข้อมูลที่ลื่นไหลแบบนี้เองครับ

แชร์บทความ

Facebook X LINE

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

คู่มือ MS-SQL Backup, Restore และการตั้งค่า Maintenance Plans สำหรับมือใหม่
MS-SQL

คู่มือ MS-SQL Backup, Restore และการตั้งค่า Maintenance Plans สำหรับมือใหม่

เรียนรู้วิธีสำรองข้อมูล (Backup) และกู้คืน (Restore) ฐานข้อมูล MS-SQL ด้วย SSMS พร้อมวิธีตั้งค่าระบบอัตโนมัติ เพื่อป้องกันข้อมูลหายสำหรับโปรแกรมเมอร์มือใหม่

3 weeks ago 7 นาที
16 views
คู่มือการจัดการความปลอดภัยใน MS-SQL: Logins, Users และการคุมสิทธิ์ด้วย GRANT, DENY, REVOKE
MS-SQL

คู่มือการจัดการความปลอดภัยใน MS-SQL: Logins, Users และการคุมสิทธิ์ด้วย GRANT, DENY, REVOKE

ไม่อยากให้ฐานข้อมูลถูกแฮกต้องอ่าน! เรียนรู้วิธีตั้งค่าความปลอดภัยใน MS-SQL ตั้งแต่การสร้าง Login (บัญชีผู้ใช้) ไปจนถึงการคุมสิทธิ์เข้าถึงข้อมูลให้ปลอดภัย

3 weeks ago 9 นาที
15 views
เจาะลึก MS-SQL Trigger (AFTER & INSTEAD OF) พร้อมวิธีจัดการ Error ด้วย TRY...CATCH
MS-SQL

เจาะลึก MS-SQL Trigger (AFTER & INSTEAD OF) พร้อมวิธีจัดการ Error ด้วย TRY...CATCH

เรียนรู้การใช้ Trigger ใน MS-SQL เพื่อจัดการข้อมูลอัตโนมัติ พร้อมเทคนิคการเขียน Error Handling ให้ระบบเสถียร ไม่พังง่าย เหมาะสำหรับมือใหม่หัดเขียนฐานข้อมูล

3 weeks ago 9 นาที
15 views