ทำความรู้จักกับ CTE ตัวช่วยให้โค้ดฐานข้อมูลอ่านง่ายขึ้น
เวลาที่เราต้องเขียน Query (คำสั่งถามข้อมูลจากฐานข้อมูล) ที่ซับซ้อนมาก ๆ โค้ดมักจะยาวจนดูไม่ออกว่ากำลังทำอะไรอยู่ CTE หรือ Common Table Expression (การสร้างตารางชั่วคราวที่มีชื่อเรียกใช้งานได้เฉพาะในคำสั่งนั้น) คือทางออกที่จะช่วยให้ชีวิตเราง่ายขึ้น เปรียบเหมือนการที่เราแยกส่วนประกอบของอาหารออกมาวางไว้บนโต๊ะก่อนจะเริ่มลงมือปรุง แทนที่จะหยิบจับทุกอย่างพร้อมกันในกระทะ
การใช้ WITH (คำสั่งเปิดตัว CTE) ช่วยให้เราแบ่งขั้นตอนการดึงข้อมูลเป็นก้อนเล็ก ๆ ได้ แทนที่จะเขียนคำสั่งซ้อนกันหลายชั้นจนตาลาย เราสามารถตั้งชื่อก้อนข้อมูลเหล่านั้นแล้วนำมาใช้ต่อได้ทันที เหมือนกับการตั้งชื่อตัวแปรในภาษาโปรแกรมทั่วไป ทำให้คนอื่นที่มาอ่านโค้ดต่อจากเราเข้าใจได้ง่ายขึ้นมาก
มือใหม่มักจะเขียน Query แบบซ้อนกันหลายชั้น (Subquery) ซึ่งอ่านยากและจัดการข้อผิดพลาดได้ลำบาก การเปลี่ยนมาใช้ CTE จะช่วยให้เรามองเห็นภาพรวมของข้อมูลได้ชัดเจนขึ้น หากเกิดปัญหาขึ้นมา เราสามารถไล่เช็กได้ทีละก้อนว่าข้อมูลผิดพลาดที่จุดไหน ทำให้การพัฒนาโปรแกรมของเรามีประสิทธิภาพและเป็นระบบมากกว่าเดิม
-- ตัวอย่างการใช้ CTE แบบง่าย
WITH UserSales AS (
-- ตั้งชื่อก้อนข้อมูลว่า UserSales
SELECT user_id, SUM(amount) as total
FROM orders
GROUP BY user_id
)
SELECT * FROM UserSales WHERE total > 1000;
-- เลือกข้อมูลจากตารางชั่วคราวที่สร้างไว้
ในโค้ดนี้ WITH จะสร้างตารางชั่วคราวชื่อ UserSales ขึ้นมาก่อน จากนั้นคำสั่ง SELECT ด้านล่างจะดึงข้อมูลจากตารางนั้นมาแสดงผลอีกที ผลลัพธ์ที่ได้คือรายชื่อผู้ใช้ที่มียอดซื้อรวมเกิน 1,000 บาท โดยที่เราไม่ต้องเขียนสูตรคำนวณซ้ำซ้อนในคำสั่งหลัก
สร้างตารางชั่วคราวด้วย WITH เพื่อความสะอาดของโค้ด
การเขียน SQL (ภาษาสำหรับจัดการฐานข้อมูล) ที่ดีต้องเน้นความอ่านง่ายและแก้ไขได้สะดวก CTE ช่วยให้เราจัดระเบียบคำสั่งที่ต้องดึงข้อมูลหลายขั้นตอนให้เป็นระเบียบเหมือนการจัดบ้าน ถ้าเรามีข้อมูลที่ต้องผ่านการกรองหลายรอบ การแยกทำทีละขั้นตอนด้วย WITH จะช่วยให้เราตรวจสอบข้อมูลแต่ละขั้นได้ว่าถูกต้องหรือไม่
ข้อควรระวังคือ CTE จะอยู่ได้แค่ในคำสั่งนั้นเท่านั้น เมื่อรันเสร็จแล้วมันจะหายไปทันที ไม่ได้ถูกเก็บลงในฐานข้อมูลจริง เหมาะสำหรับการนำข้อมูลมาพักไว้ชั่วคราวเพื่อรอการประมวลผลต่อ ไม่ใช่การสร้างตารางถาวรไว้ใช้งานในระยะยาว ดังนั้นไม่ต้องกังวลเรื่องพื้นที่เก็บข้อมูลหรือการสร้างขยะในระบบ
สำหรับมือใหม่ที่เพิ่งเริ่มหัดเขียน อย่าลืมตั้งชื่อตารางชั่วคราวให้สื่อความหมาย เพื่อให้เพื่อนร่วมทีมหรือตัวเราเองในอนาคตกลับมาอ่านแล้วเข้าใจได้ทันที การตั้งชื่อแบบสื่อความหมายดีกว่าการใช้ชื่อย่อที่ไม่มีใครรู้เรื่อง เป็นวินัยที่ดีในการเป็นโปรแกรมเมอร์มืออาชีพที่ใคร ๆ ก็อยากร่วมงานด้วย
-- การใช้ CTE หลายตารางพร้อมกัน
WITH TopUsers AS (
SELECT user_id FROM users WHERE status = 'active'
),
RecentOrders AS (
SELECT user_id, order_date FROM orders WHERE order_date > '2023-01-01'
)
SELECT * FROM TopUsers JOIN RecentOrders ON TopUsers.user_id = RecentOrders.user_id;
-- นำข้อมูลจากสองตารางชั่วคราวมาเชื่อมกัน
บรรทัดแรกคือการสร้างตาราง TopUsers และ RecentOrders พร้อมกันโดยใช้เครื่องหมายคอมมาคั่น เราสามารถเรียกใช้ตารางทั้งสองนี้ในคำสั่ง SELECT สุดท้ายได้เลย ผลลัพธ์ที่ได้คือรายชื่อผู้ใช้งานที่ยังเปิดใช้งานอยู่และมีประวัติการสั่งซื้อในปี 2023 ขึ้นไป
เจาะลึก Recursive CTE การดึงข้อมูลแบบลำดับขั้น
บางครั้งข้อมูลในฐานข้อมูลของเราไม่ได้เก็บแบบเรียงหน้ากระดาษ แต่เก็บแบบเป็นสายสัมพันธ์ เช่น แผนผังองค์กรหรือหมวดหมู่สินค้าที่มีหมวดหมู่ย่อย Recursive CTE (การสร้างตารางชั่วคราวที่เรียกใช้ตัวเองซ้ำ ๆ) คือเครื่องมือที่ใช้ดึงข้อมูลเหล่านี้ออกมาได้อย่างสวยงาม เปรียบเหมือนการเดินไล่สายลำดับญาติจากปู่ไปพ่อแล้วถึงลูก
หลักการทำงานของมันคือการแบ่งเป็นสองส่วน ส่วนแรกคือ Anchor Member (จุดเริ่มต้นของการค้นหา) และส่วนที่สองคือ Recursive Member (ส่วนที่สั่งให้ทำงานซ้ำจนกว่าจะครบ) มันจะทำงานวนลูปไปเรื่อย ๆ จนกว่าเงื่อนไขที่เรากำหนดไว้จะเป็นเท็จ หรือจนกว่าจะไม่มีข้อมูลให้ค้นหาต่ออีกแล้ว
นี่คือหัวใจสำคัญของการจัดการข้อมูลที่มีโครงสร้างซับซ้อน หากเราไม่ใช้ Recursive CTE เราอาจจะต้องเขียนโปรแกรมวนลูปในภาษาฝั่ง Server (เช่น Python หรือ Node.js) หลายสิบรอบ ซึ่งกินทรัพยากรเครื่องมากกว่าการให้ฐานข้อมูลจัดการให้เสร็จสรรพตั้งแต่ต้นทาง
-- ตัวอย่างการดึงข้อมูลลำดับขั้น (สมมติเป็นโครงสร้างองค์กร)
WITH RECURSIVE OrgChart AS (
SELECT id, name, manager_id FROM employees WHERE manager_id IS NULL -- จุดเริ่มต้น
UNION ALL
SELECT e.id, e.name, e.manager_id FROM employees e
JOIN OrgChart o ON e.manager_id = o.id -- การวนลูปดึงลูกน้อง
)
SELECT * FROM OrgChart;
บรรทัดแรกเริ่มจากดึงหัวหน้าใหญ่ที่ไม่มี manager_id ออกมา ส่วน UNION ALL จะนำผลลัพธ์ใหม่ไปเชื่อมกับข้อมูลเดิมเรื่อย ๆ จนครบองค์กร ผลลัพธ์ที่ได้คือรายชื่อพนักงานที่ถูกจัดเรียงตามลำดับชั้นจากบนลงล่างอย่างครบถ้วน
ข้อควรระวังในการใช้ Recursive CTE เพื่อป้องกันระบบค้าง
จุดที่มือใหม่มักจะพลาดที่สุดเวลาใช้ Recursive CTE คือการลืมใส่เงื่อนไขหยุด หรือเขียนเงื่อนไขที่ไม่มีวันเป็นจริง ทำให้ระบบทำงานวนลูปไม่รู้จบ (Infinite Loop) จนฐานข้อมูลค้างหรือกินหน่วยความจำจนเต็ม เปรียบเหมือนการที่เราข้ามถนนโดยไม่ดูรถจนเกิดอุบัติเหตุ
วิธีป้องกันคือต้องมั่นใจว่าในส่วน Recursive Member มีการเพิ่มเงื่อนไขที่ทำให้ข้อมูลค่อย ๆ หมดไป หรือมีการจำกัดจำนวนชั้นที่ต้องการดึงข้อมูล เช่น การใส่คำสั่ง LIMIT หรือการตรวจสอบค่า level ว่าไม่เกินที่กำหนดไว้ เพื่อให้ระบบรู้ว่าเมื่อไหร่ควรหยุดทำงาน
การฝึกเขียน Recursive CTE ให้เก่ง ต้องเริ่มจากการวาดภาพข้อมูลในกระดาษก่อนว่าเราต้องการดึงอะไรบ้าง และจุดไหนคือจุดที่การวนลูปควรจะสิ้นสุด การมีแผนผังในหัวก่อนลงมือพิมพ์โค้ดจะช่วยลดข้อผิดพลาดได้มหาศาล และทำให้เราเป็นโปรแกรมเมอร์ที่ทำงานได้อย่างปลอดภัยและมีประสิทธิภาพ
-- การใส่เงื่อนไขป้องกันลูปไม่รู้จบ
WITH RECURSIVE Counter AS (
SELECT 1 AS n -- เริ่มที่ 1
UNION ALL
SELECT n + 1 FROM Counter WHERE n < 5 -- หยุดเมื่อครบ 5
)
SELECT * FROM Counter;
ตัวอย่างนี้สร้างเลข 1 ถึง 5 โดยใช้เงื่อนไข WHERE n < 5 ในการควบคุม ถ้าเราลืมใส่บรรทัดนี้ โปรแกรมจะรันไปเรื่อย ๆ จนกว่าเครื่องจะพัง ผลลัพธ์ที่ได้คือตัวเลข 1, 2, 3, 4, 5 เรียงกันลงมา
เส้นทางการเรียนรู้สำหรับมือใหม่
การจะเก่งเรื่องฐานข้อมูลไม่ใช่เรื่องของการจำคำสั่งได้ทั้งหมด แต่คือการเข้าใจว่าข้อมูลแต่ละชุดมีความสัมพันธ์กันอย่างไร หากคุณเพิ่งเริ่มหัด ให้ลองฝึกดึงข้อมูลจากตารางเดียวด้วย WITH ก่อน แล้วค่อยขยับไปทำ JOIN (การนำข้อมูลจากสองตารางมาเชื่อมกัน) แล้วค่อยปิดท้ายด้วย Recursive CTE
อย่าพยายามเรียนทุกอย่างในวันเดียว ให้ลองหาโจทย์จริงทำ เช่น การทำระบบจัดการสินค้าที่มีหมวดหมู่ย่อย การลองผิดลองถูกด้วยตัวเองมีค่ามากกว่าการอ่านตำราเป็นสิบเล่ม ถ้าโค้ดพังให้ดูข้อความแจ้งเตือน (Error Message) เพราะนั่นคือครูที่ดีที่สุดที่จะบอกว่าเราลืมอะไรไป หรือเขียนผิดตรงไหน
สำหรับคนที่อยากก้าวไปเป็นโปรแกรมเมอร์มืออาชีพ การทำความเข้าใจ CTE จะช่วยให้คุณมีทักษะในการจัดการข้อมูลที่ซับซ้อนได้ดีกว่าคนอื่น ซึ่งเป็นสิ่งที่บริษัทต้องการมากในการคัดเลือกคนเข้าทำงาน เพราะมันสะท้อนว่าคุณสามารถเขียนโค้ดที่สะอาดและยั่งยืนได้ ไม่ใช่แค่เขียนให้เสร็จไปวัน ๆ
สรุปการนำไปใช้จริงในงานพัฒนาซอฟต์แวร์
เมื่อคุณต้องสร้างระบบ เช่น ระบบจัดการคลังสินค้าที่มีการแบ่งหมวดหมู่ย่อย หรือระบบโครงสร้างบริษัท CTE และ Recursive CTE คือเครื่องมือหลักที่จะช่วยให้คุณดึงข้อมูลเหล่านั้นออกมาใช้งานได้อย่างรวดเร็ว แทนที่จะเขียนโค้ดภาษาโปรแกรมซับซ้อนเพื่อจัดเรียงข้อมูล คุณสามารถให้ฐานข้อมูลจัดการให้ได้ทั้งหมด
การฝึกฝนเรื่องนี้จะทำให้คุณมองเห็นภาพรวมของข้อมูลได้ดีขึ้น คุณจะเริ่มเขียนโค้ดที่สั้นลงแต่มีประสิทธิภาพมากขึ้น ซึ่งเป็นคุณสมบัติของโปรแกรมเมอร์ที่เก่งและมีวินัย การเขียนโค้ดที่ดีไม่ใช่แค่การใช้คำสั่งที่ล้ำสมัย แต่คือการใช้เครื่องมือให้เหมาะสมกับงานและเข้าใจธรรมชาติของข้อมูลอย่างแท้จริง
สรุปสั้น ๆ คือ WITH เอาไว้จัดระเบียบโค้ดให้สะอาด ส่วน WITH RECURSIVE เอาไว้จัดการข้อมูลที่มีลำดับชั้น ถ้าคุณเข้าใจสองตัวนี้ คุณจะพบว่าปัญหาที่เคยดูยากจะกลายเป็นเรื่องง่ายทันที ขอให้สนุกกับการเขียนโค้ดและพัฒนาตัวเองต่อไปในเส้นทางนี้