Back to notes
mastery-data-sql
Featured

Advanced SQL Mastery: เปลี่ยน Query นรกให้เป็นงานศิลป์

เลิกเขียน Subquery ซ้อนกัน 5 ชั้น! เจาะลึก Common Table Expressions (CTE) และ Window Functions เพื่อ Query ข้อมูลที่ซับซ้อนได้อย่างสง่างาม

January 30, 20263 min read readNNexis by Seereen

🛑 1. The Problem First: "Subquery นรก" (The Pyramid of Doom)

ลองนึกถึงวันที่คุณต้องดึงข้อมูลสรุปยอดขายที่ซับซ้อน แล้วโค้ด SQL ของคุณออกมาหน้าตาแบบนี้:

HLJS SQL
-- ❌ Naive Approach: Subquery ซ้อนกันจนอ่านไม่ออก
SELECT * FROM (
  SELECT user_id, total FROM (
    SELECT user_id, SUM(amount) as total FROM orders GROUP BY user_id
  ) as t1 WHERE total > 1000
) as t2 JOIN users ON t2.user_id = users.id;
-- 🌋 พัง! แค่เห็นก็ปวดหัวแล้ว ถ้าต้อง Debug หรือเพิ่มเงื่อนไข คุณจะหาจุดเริ่มไม่เจอ

ปัญหา: ยิ่ง Business Logic ซับซ้อน SQL ของคุณจะยิ่งยาวและซ้อนกันหลายชั้น สิ่งนี้ทำให้การบำรุงรักษา (Maintenance) เป็นเรื่องยากมาก และที่ร้ายกว่านั้นคือ ประสิทธิภาพ (Performance) อาจจะแย่ลงเพราะ Browser/DB Optimizer ต้องประมวลผล Subquery ซ้ำซ้อน นี่คือที่มาของ Technical Debt ในระดับ Database ครับ


💡 2. Real-Life Analogy: การต่อจิ๊กซอว์ vs การวางกองขยะ

  • Subqueries: เหมือนการ "ยัดทุกอย่างลงไปในถุงเดียว". คุณต้องรื้อถุงชั้นในสุดออกมาดูถึงจะรู้ว่าข้างในคืออะไร
  • CTE (WITH): เหมือนการ "จัดของใส่กล่องติดป้ายกำกับ". คุณเตรียมกล่องที่ 1 (UserSales), กล่องที่ 2 (TopCustomer) แล้วค่อยเอาของในกล่องมาวางเรียงกันตอนจบ (Step-by-step)
  • Window Functions: เหมือนการ "ถือไม้บรรทัดวัดระยะสะสม". คุณไม่ต้องหยิบของออกมานับใหม่ทีละชิ้น คุณแค่ลากไม้บรรทัดผ่านกองของ แล้วบันทึกยอดสะสมไปด้วยในตัว (Running Total)

🚀 3. Execution Journey: ขั้นตอนการดึงข้อมูลแบบ Senior

เราจะใช้ CTE เพื่อให้อ่านง่าย และ Window Functions เพื่อคำนวณข้ามบรรทัด

🛠 Step-by-step:

  1. Define CTE: สร้างตารางชั่วคราวที่มีชื่อเรียกชัดเจน (เหมือนประกาศตัวแปรใน JS)
  2. Apply Window Function: ใช้ OVER() เพื่อคำนวณ Ranking หรือ Sum โดยไม่ต้องใช้ GROUP BY ที่โหดร้าย
  3. Consolidate: รวมข้อมูลจาก CTE ทั้งหมดมาเป็นผลลัพธ์สุดท้าย
HLJS SQL
// ✅ Best Practice: ใช้ CTE และ Window Function เพื่อความคลีน
WITH MonthlySales AS (
  SELECT
    product_name,
    category,
    amount,
    -- 🛠 ใช้ Window Function หา Ranking ในแต่ละหมวดหมู่
    ROW_NUMBER() OVER(PARTITION BY category ORDER BY amount DESC) as rank
  FROM sales
)
SELECT * FROM MonthlySales WHERE rank <= 3; // 🛠 ดึงแค่ Top 3 ของทุกหมวดหมู่ จบในที่เดียว!

🪤 4. The Junior Trap: โรค "Self-Join เพื่อหาผลรวม"

จูเนียร์มักจะใช้วิธี Join ตารางตัวเองซ้ำๆ เพื่อหาค่าสะสมหรือค่าเฉลี่ย:

HLJS SQL
-- ❌ Junior Trap: กินทรัพยากร DB มหาศาล
SELECT s1.date, s1.amount, (SELECT SUM(amount) FROM sales s2 WHERE s2.date <= s1.date) as total
FROM sales s1;
-- 🌋 พัง! ถ้ามีข้อมูล 1 ล้านบรรทัด DB จะต้องวนลูปคำนวณวนไปวนมาเป็นล้านๆ ครั้ง (O(N^2))

ระวัง: การทำ Subquery ใน SELECT clause คือการฆ่าตัวตายในระดับ SQL ✅ การแก้ไข: ใช้ Window Function SUM(amount) OVER (ORDER BY date) ซึ่งทำงานได้เร็วกว่าหลักพันเท่าเพราะมันวนลูปแค่รอบเดียว (O(N))


⚖️ 5. The Why Matrix: Subquery vs CTE vs Window Func

หัวข้อSubquery (แบบเก่า)CTE (แบบใหม่)Window Function (ขั้นเทพ)
ความอ่านง่าย🐢 ยากนรกแตก⚡⚡⚡ เหมือนอ่านนิยาย⚡⚡ กระชับมาก
Performanceต่ำ (ถ้าซ้อนเยอะ)ปานกลาง/สูง (ขึ้นอยู่กับ DB)🚀 สูงสุด สำหรับงานคำนวณข้ามแถว
ความยืดหยุ่นต่ำ⚡⚡⚡ สูงมาก (เรียกใช้ซ้ำได้)⚡⚡ สูง (ทำ Ranking ง่าย)
เหมาะกับงาน Simple CRUDงาน Logic ซับซ้อนหลายชั้นงาน Analytics / Reporting

🎓 6. Senior Mindset Summary

การเป็น Senior คือการมองว่า "SQL ไม่ใช่แค่ภาษาสำหรับดึงข้อมูล แต่เป็นพลังในการประมวลผลที่ทรงพลังที่สุด". การย้ายลอจิกบางส่วนมาคำนวณที่ Database ด้วยท่าที่สง่างาม ไม่เพียงแต่จะทำให้โค้ดแอปของคุณสะอาดขึ้น แต่ยังทำให้ระบบของคุณรวดเร็วปานกามนิตหนุ่มครับ!

Share this note

© 2026 My Notes by Seereen