🗄️ MySQL/MariaDB

จัดการ SQL Query ซับซ้อนให้เป็นระเบียบด้วย CTE (Common Table Expression) ใน MySQL และ MariaDB

9 นาที 19 views บันทึกเป็น PDF
จัดการ SQL Query ซับซ้อนให้เป็นระเบียบด้วย CTE (Common Table Expression) ใน MySQL และ MariaDB

เบื่อไหมกับ SQL ยาวเหยียดที่อ่านไม่รู้เรื่อง? มาเรียนรู้วิธีใช้ CTE (ตารางชั่วคราว) เพื่อจัดระเบียบ Query ให้สะอาดตาและใช้ Recursive CTE จัดการข้อมูลลำดับชั้นได้ง่ายขึ้น

ทำความรู้จักกับ CTE ตัวช่วยจัดการ Query ให้สะอาดตา

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

-- สร้าง CTE ชื่อ sales_summary เพื่อหาผลรวมยอดขาย
WITH sales_summary AS (
    SELECT product_id, SUM(amount) as total_sales
    FROM orders
    GROUP BY product_id
)
-- ดึงข้อมูลจาก CTE ที่สร้างไว้มาใช้งาน
SELECT * FROM sales_summary WHERE total_sales > 1000;

คำอธิบายโค้ด:

  • บรรทัดที่ 2: ใช้คำสั่ง WITH เพื่อตั้งชื่อ CTE ว่า sales_summary
  • บรรทัดที่ 3-5: เขียนคำสั่ง SELECT ปกติเพื่อหาผลรวมยอดขายตามสินค้า
  • บรรทัดที่ 7: เรียกใช้งาน sales_summary เหมือนกับว่าเป็นตารางปกติในฐานข้อมูล

ผลลัพธ์ที่ควรเห็น: ตารางข้อมูลที่แสดง product_id และ total_sales เฉพาะรายการที่มียอดขายมากกว่า 1,000

โครงสร้างการเขียนคำสั่ง WITH ที่ถูกต้อง

การเริ่มต้นเขียน CTE ไม่มีอะไรซับซ้อนครับ เราแค่ใช้คำสั่ง WITH ตามด้วยชื่อตารางชั่วคราวที่คุณตั้งเอง แล้วใส่ AS ตามด้วยวงเล็บเปิดและปิดที่ข้างในบรรจุคำสั่ง Query ที่คุณต้องการไว้ เมื่อจบการประกาศ CTE แล้ว คุณสามารถใช้ชื่อนั้นได้ทันทีในคำสั่ง SELECT, INSERT หรือ UPDATE จุดที่มือใหม่มักจะพลาดคือการลืมใส่คอมม่า (,) หากคุณต้องการสร้าง CTE หลายตัวในคำสั่งเดียว คุณต้องคั่นแต่ละตัวด้วยเครื่องหมายคอมม่าเสมอ นอกจากนี้ชื่อของ CTE ต้องไม่ซ้ำกับชื่อตารางที่มีอยู่จริงในฐานข้อมูล เพื่อป้องกันความสับสนของระบบเวลาทำงาน จำไว้ว่า CTE จะมีชีวิตอยู่แค่ในคำสั่งนั้นๆ เท่านั้น เมื่อคำสั่งรันเสร็จ ข้อมูลชั่วคราวเหล่านี้จะหายไปทันที ไม่ต้องกลัวว่าจะไปหนักเครื่องหรือรกฐานข้อมูล การฝึกใช้บ่อยๆ จะช่วยให้คุณจัดลำดับความคิดในการเขียนโค้ดได้เป็นระบบมากขึ้นครับ

-- สร้าง CTE สองตัวพร้อมกัน
WITH top_products AS (
    SELECT id FROM products WHERE price > 500
),
high_sales AS (
    SELECT product_id FROM orders WHERE amount > 100
)
-- นำทั้งสอง CTE มาเชื่อมกัน
SELECT * FROM top_products 
JOIN high_sales ON top_products.id = high_sales.product_id;

คำอธิบายโค้ด:

  • บรรทัดที่ 2-4: สร้าง top_products เพื่อเก็บรายการสินค้าที่มีราคาสูง
  • บรรทัดที่ 5-7: สร้าง high_sales เพื่อเก็บรายการสินค้าที่ขายได้เยอะ
  • บรรทัดที่ 9-10: นำข้อมูลจากทั้งสอง CTE มาเชื่อมกันด้วย JOIN (คำสั่งเชื่อมตาราง)

ผลลัพธ์ที่ควรเห็น: รายการสินค้าที่มีทั้งราคาสูงและยอดขายดีปรากฏออกมาเป็นตารางเดียว

Recursive CTE คืออะไรและทำไมต้องสนใจ

Recursive CTE (การทำซ้ำแบบเรียกตัวเอง) คือความสามารถพิเศษของ CTE ที่สามารถเรียกใช้งานตัวเองซ้ำๆ ได้จนกว่าเงื่อนไขจะครบ มันมีประโยชน์มากสำหรับการจัดการข้อมูลที่เป็นลำดับชั้น เช่น ผังองค์กร หรือหมวดหมู่สินค้าที่มีหมวดหมู่ย่อยซ้อนกันไปเรื่อยๆ เปรียบเทียบง่ายๆ เหมือนกับการที่คุณเดินขึ้นบันไดทีละขั้น คุณต้องรู้ว่าตัวเองอยู่ที่ขั้นไหน และมีเงื่อนไขว่าถ้าถึงชั้นสุดท้ายให้หยุด Recursive CTE ก็ทำงานแบบเดียวกัน คือมีจุดเริ่มต้น (Anchor Member) และส่วนที่ทำซ้ำ (Recursive Member) เพื่อไต่ระดับข้อมูลขึ้นไปเรื่อยๆ จนครบตามที่ต้องการ สำหรับมือใหม่ที่เพิ่งเริ่มหัดเขียนโค้ด เรื่องนี้อาจจะดูยากในตอนแรก แต่ถ้าคุณเข้าใจหลักการว่ามันคือการ Loop (การทำงานซ้ำๆ) ในรูปแบบของคำสั่งฐานข้อมูล คุณจะเห็นพลังของมันทันที เพราะปกติถ้าเราเขียนโค้ดดึงข้อมูลลำดับชั้นแบบนี้ผ่านภาษาโปรแกรมทั่วไป เราอาจจะต้องเขียนคำสั่งหลายร้อยบรรทัด แต่ Recursive CTE ช่วยให้จบได้ในไม่กี่บรรทัดครับ

ขั้นตอนการเขียน Recursive CTE

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

-- ตัวอย่างการนับเลข 1 ถึง 5 แบบ Recursive
WITH RECURSIVE counter AS (
    SELECT 1 AS n -- จุดเริ่มต้น
    UNION ALL
    SELECT n + 1 FROM counter WHERE n < 5 -- ส่วนที่ทำซ้ำ
)
SELECT * FROM counter;

คำอธิบายโค้ด:

  • บรรทัดที่ 2: เริ่มต้นด้วยเลข 1
  • บรรทัดที่ 4: ใช้ UNION ALL เพื่อวนลูปเพิ่มค่า n ทีละ 1
  • บรรทัดที่ 5: เงื่อนไข WHERE n < 5 คือตัวบอกให้หยุดทำงานเมื่อถึงเลข 5

ผลลัพธ์ที่ควรเห็น: ตัวเลข 1, 2, 3, 4, 5 เรียงกันลงมาในคอลัมน์ n

ความแตกต่างระหว่าง MySQL และ MariaDB

ถึงแม้ทั้งคู่จะเป็นพี่น้องที่ใช้คำสั่งพื้นฐานเหมือนกัน แต่ในเรื่องของ Recursive CTE ทั้งสองตัวมีประวัติการพัฒนาที่ต่างกันเล็กน้อย โดย MariaDB รองรับฟีเจอร์นี้มาก่อน ส่วน MySQL เพิ่งจะรองรับแบบเต็มตัวในเวอร์ชัน 8.0 ขึ้นไป ดังนั้นก่อนจะนำไปใช้จริง ต้องเช็คเวอร์ชันของฐานข้อมูลที่คุณใช้อยู่เสมอ หากคุณกำลังฝึกฝนอยู่ ให้ลองติดตั้ง Docker (เครื่องมือจำลองสภาพแวดล้อมการทำงาน) เพื่อรัน MySQL หรือ MariaDB เวอร์ชันล่าสุด จะได้มั่นใจว่าคำสั่งที่เรียนรู้ไปใช้งานได้จริง ไม่ต้องกังวลเรื่องปัญหาความเข้ากันได้ของเวอร์ชันเก่าๆ ที่อาจจะไม่รองรับคำสั่งเหล่านี้ การที่คนสายโปรแกรมเมอร์ต้องรู้เรื่องเหล่านี้ เพราะในโลกการทำงานจริง เราไม่ได้เลือกใช้ฐานข้อมูลตามใจชอบ บางบริษัทอาจใช้ตัวเก่า บางที่ใช้ตัวใหม่ การเข้าใจความแตกต่างพื้นฐานจะทำให้คุณย้ายไปทำงานที่ไหนก็ได้โดยไม่ต้องปรับตัวมากเกินไปครับ

สรุป: การนำไปใช้จริงให้เก่งขึ้น

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

แชร์บทความ

Facebook X LINE

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

MySQL/MariaDB

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

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

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

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

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

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

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

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

1 month ago 8 นาที
23 views