🔵 PostgreSQL

PostgreSQL CTE & Recursive CTE การสร้างตารางชั่วคราวด้วย WITH (CTE) และการดึงข้อมูลแบบลำดับขั้นซ้ำๆ (Hierarchical Data) ด้วย WITH RECURSIVE

10 นาที 22 views บันทึกเป็น PDF
PostgreSQL CTE & Recursive CTE การสร้างตารางชั่วคราวด้วย WITH (CTE) และการดึงข้อมูลแบบลำดับขั้นซ้ำๆ (Hierarchical Data) ด้วย WITH RECURSIVE

ทำความรู้จักกับ CTE ตัวช่วยให้โค้ดฐานข้อมูลอ่านง่ายขึ้น...

ทำความรู้จักกับ 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 เอาไว้จัดการข้อมูลที่มีลำดับชั้น ถ้าคุณเข้าใจสองตัวนี้ คุณจะพบว่าปัญหาที่เคยดูยากจะกลายเป็นเรื่องง่ายทันที ขอให้สนุกกับการเขียนโค้ดและพัฒนาตัวเองต่อไปในเส้นทางนี้

แชร์บทความ

Facebook X LINE

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

จัดการ PostgreSQL เบื้องต้น: การคุมสิทธิ์ User, การ Backup และ Restore ข้อมูลบน Windows
PostgreSQL

จัดการ PostgreSQL เบื้องต้น: การคุมสิทธิ์ User, การ Backup และ Restore ข้อมูลบน Windows

มือใหม่ต้องรู้! วิธีจัดการสิทธิ์ผู้ใช้งาน (Role) และการสำรองข้อมูล (Backup) ใน PostgreSQL ให้ปลอดภัย พร้อมวิธี Restore ข้อมูลกลับมาใช้ได้จริงผ่าน Command Line

1 month ago 8 นาที
27 views
เจาะลึก PostgreSQL Triggers: วิธีเขียนคำสั่งทำงานอัตโนมัติเมื่อข้อมูลเปลี่ยน
PostgreSQL

เจาะลึก PostgreSQL Triggers: วิธีเขียนคำสั่งทำงานอัตโนมัติเมื่อข้อมูลเปลี่ยน

อยากให้ฐานข้อมูลทำงานอัตโนมัติเมื่อมีการเพิ่มหรือแก้ไขข้อมูลไหม? มาเรียนรู้การใช้ PostgreSQL Triggers (ตัวสั่งการอัตโนมัติ) ทั้ง BEFORE และ AFTER เพื่อลดงานซ้ำซ้อน

1 month ago 8 นาที
22 views
เจาะลึก PostgreSQL PL/pgSQL: เขียนฟังก์ชันและ Stored Procedures ฉบับมือใหม่
PostgreSQL

เจาะลึก PostgreSQL PL/pgSQL: เขียนฟังก์ชันและ Stored Procedures ฉบับมือใหม่

เรียนรู้วิธีเขียนโปรแกรมในฐานข้อมูลด้วย PL/pgSQL ทั้งการสร้างฟังก์ชัน การใช้ตัวแปร และการทำ Loop เพื่อให้งานจัดการข้อมูลของคุณเร็วและเป็นระเบียบยิ่งขึ้น

1 month ago 8 นาที
20 views