← Back to PostgreSQL course
PHASE 2: DEEP DIVE INTO THE 4 PILLARS OPTIMIZATION ENGINE 🚀

Day 9: Vectorized Processing, Set-Based Logic & Parallel Query Optimization (การประมวลผลเชิงกลุ่มข้อมูล Set-Based Logic, การขจัดคอขวด Procedural RBAR Loop, และการดึงพลัง Multi-core CPU ด้วย Parallel Query Optimization)

เปลี่ยนผ่าน Mindset จากการเขียนโค้ดวนลูปทีละแถว (Procedural RBAR - Row-By-Agonizing-Row) ที่บล็อกเซิร์ฟเวอร์ให้วิ่งแค่ Single-Core นาน 14 ชั่วโมง มาเป็นการคิดเชิงเซต (Set-Based Logic) เปิดทางให้ PostgreSQL ทำ Vectorized Processing ประมวลผลข้อมูลใน Memory เป็นบล็อก และปลดล็อก Multi-Core CPU ด้วย Parallel Query Optimization ลดเวลาเหลือเพียง 8 วินาที!

📍 ตำแหน่งปัจจุบันของคุณบน Roadmap (18-Day Masterclass) Phase 2: Final Pillar Optimization (จบ Phase 2 วันนี้!)
Phase 1: Architecture Synthesis (Days 1–4) ✓
➔
Day 5: Pillar 1 - UDFs & Inlining ✓
➔
Day 6: Pillar 2 - Stored Procedures ✓
➔
Day 7: Pillar 3 - Event Triggers ✓
➔
Day 8: Pillar 4 - Pre-computation ✓
➔
Day 9: Set-Based & Parallel Query
➔
Day 10: Phase 2 Synthesis & Capstone
เปิดบท

ถอดรหัสคำสำคัญของวันนี้

สี่เสาหลักสร้างได้แล้ว แต่ถ้ายังเขียนลูปวนทีละแถว ฐานข้อมูลจะใช้ CPU ได้แค่คอร์เดียว วันนี้คือการเลิกคิดแบบแถวต่อแถว แล้วเขียน SQL ให้ทำงานเป็นชุด

Vectorized Processing

กลไกของเอนจินที่ดึงและประมวลผลข้อมูลเป็นชุดบล็อกหรืออาร์เรย์ใน memory registers แทนที่จะค่อยๆ ดึงทีละแถว

Set-Based Logic

การเขียน SQL ให้จัดการกลุ่มข้อมูลทั้งหมดในครั้งเดียว ตรงข้ามกับ RBAR (Row-By-Agonizing-Row) ที่เอาลูปมาวนอ่านทีละแถว

Parallel Query Optimization

ความสามารถของ Query Planner ในการแบ่งสแกนข้อมูลขนาดใหญ่ ให้ CPU หลายคอร์ช่วยกันประมวลผลพร้อมกัน

โดยรวมวันนี้คือการเลิกเขียนลูปวนทีละแถว เปลี่ยนมาใช้คำสั่ง SQL เชิงเซตคำสั่งเดียว เพื่อเปิดทางให้ฐานข้อมูลทำ Vectorized Processing และใช้ทุก CPU core สแกนข้อมูลขนานกัน

เปรียบกับชีวิตจริง

เหมือนทาสีรั้วบ้าน 100 แผ่น แบบ RBAR คือจ้างคนหนึ่งคนถือพู่กันเดินทาทีละแผ่น แบบ Set-Based Parallel คือเอาเครื่องพ่นสีขนาดใหญ่มาฉีดพ่นทีเดียวทุกแผ่น โดยมีคนงานสี่คนช่วยกันพ่น

รวบยอดเทคนิคการเขียน SQL ใน Phase 2 เปลี่ยนโค้ดที่รันนานสิบสี่ชั่วโมง ให้เหลือเพียงไม่กี่วินาที โดยไม่ต้องเพิ่มฮาร์ดแวร์

1

🏛️ มโนทัศน์ สถาปัตยกรรมองค์กร และสถานการณ์จริง (Concept & Architecture)

เมื่อเราก้าวผ่านการสร้าง Stored Units ทั้ง 4 เสาหลัก (UDFs, Procedures, Triggers, Materialized Views) ปัญหาคลาสสิกต่อมาของนักพัฒนาที่ย้ายสายมาจาก Imperative Programming (เช่น Java, C#, Python) คือการเขียนโค้ดสไตล์ Procedural RBAR (Row-By-Agonizing-Row) หรือการเอา LOOP / CURSOR มาวนอ่านและอัปเดตข้อมูลทีละแถว ซึ่งทำลายสถาปัตยกรรมฐานข้อมูลเชิงสัมพันธ์อย่างร้ายแรง!

🚨 วิกฤตคำนวณเบี้ยปรับสินเชื่อ 20,000,000 บัญชี: รันข้ามคืน 14 ชั่วโมง CPU ติดอยู่ Core เดียว!

ระบบคำนวณเบี้ยปรับดอกเบี้ยผิดนัดชำระและปรับปรุงระดับชั้นลูกหนี้ (Credit Scoring Engine) สำหรับสถาบันการเงินแห่งหนึ่ง ต้องประมวลผลบัญชีสินเชื่อจำนวน 20,000,000 บัญชี นักพัฒนาเขียน PL/pgSQL โดยใช้ CURSOR เปิดวนลูป LOOP ทีละ 1 แถวเพื่อเช็คเงื่อนไข แล้วสั่ง UPDATE accounts SET penalty = ... WHERE account_id = v_rec.account_id; วนซ้ำ 20 ล้านรอบ:

14 ชั่วโมง
รันข้ามคืนล้นเกิน Batch Window ชนเวลาเปิดสาขา
12.5% CPU
รันแน่นแค่ Core เดียว (อีก 7 Cores บนเครื่อง 8-Cores ว่างเปล่า)
20M Context Switches
สลับข้ามไปมาระหว่าง PL/pgSQL กับ SQL Engine 20 ล้านรอบ

ต้นตอของวิกฤตนี้คือ RBAR Anti-Pattern บังคับให้ฐานข้อมูลประมวลผลแบบ Synchronous Single-threaded ทีละแถวตามลำดับ ทำให้ Query Planner ไม่สามารถแบ่งงานแบบ Parallel Execution ได้เลย! ทางออกเดียวคือการเปลี่ยน Mindset มาใช้ Set-Based Vectorized Processing ครับ!

🏭 อุปมาอุปไมย: สายพานตรวจไข่ไก่ทีละฟองด้วยตาเปล่า vs เครื่องสแกนคอนเทนเนอร์ 4 ช่องทางพร้อมกัน

❌ Procedural RBAR (คนงานยืนหยิบไข่ไก่ตรวจทีละฟอง 20 ล้านครั้ง)

เปรียบเสมือนโรงงานมีไข่ไก่ 20 ล้านฟอง แล้วจ้างคนงานคนเดียวยืนหยิบไข่ขึ้นมาส่องไฟทีละฟอง ตรวจเสร็จแปะสติกเกอร์ทีละฟอง แล้ววางลงกล่อง (เหมือน FOR rec IN CURSOR LOOP ... UPDATE)

  • คนงานคนนั้นเหนื่อยแทบขาดใจ (Core ที่ 1 วิ่ง 100%) ในขณะที่เพื่อนคนงานอีก 7 คนนั่งเล่นมือถือเฉยๆ (อีก 7 Cores ไม่ได้ใช้)
  • เสียเวลาสลับไปมาระหว่างหยิบกับแปะสติกเกอร์ 20 ล้านรอบ (Context Switching Overhead)
  • ใช้เวลา 14 ชั่วโมง งานก็ยังไม่เสร็จ รถขนส่งมารอรับสินค้าไม่ได้!
✅ Set-Based Vectorized (เทไข่ผ่านแท่นสแกนเซนเซอร์ความเร็วสูง 4 ช่องทาง)

ผู้จัดการเปลี่ยนระบบใหม่เป็นแท่นสแกนเซนเซอร์คู่ขนาน เทไข่ผ่านเป็นแผงขนาดใหญ่ (Vectorized Memory Block) แล้วกระจายลง 4 ช่องทางสแกนอัตโนมัติ (Parallel Workers) พร้อมกันในระดับมิลลิวินาที

  • ดึงกำลังทุก CPU Cores มาช่วยทำงานพร้อมกันเต็มประสิทธิภาพ 100% (Parallel Gather Scan)
  • สแกนอ่านและประทับตรายอดเบี้ยปรับจบใน Relational Operation เดียว (Single Pass Scan)
  • งาน 20 ล้านแถวเสร็จสมบูรณ์ในเวลาเพียง 8 วินาที (เร็วขึ้นกว่า 6,000 เท่าตัว!)

⚖️ การเปรียบเทียบเชิงสถาปัตยกรรม: Procedural RBAR Loop vs. Set-Based Vectorized Processing

มิติเปรียบเทียบสถาปัตยกรรม Procedural RBAR (FOR LOOP / CURSOR) Set-Based Vectorized (UPDATE / MERGE / CTE)
กระบวนทัศน์การประมวลผล (Paradigm) Imperative Procedural (สั่งทีละขั้นตอน ทีละแถว) Declarative Relational (ระบุผลลัพธ์เชิงเซตที่ต้องการ)
การใช้ประโยชน์จาก CPU Cores ❌ ติดอยู่ Core เดียว (Single-threaded 12.5%) ✅ กระจายทุก Cores ผ่าน Parallel Gather Workers
Context Switching Overhead สูงมาก (สลับระหว่าง PL Engine กับ SQL Engine $N$ รอบ) ต่ำมาก (ประมวลผลจบภายใน Relational Engine รอบเดียว)
การอ่าน Data Pages บนดิสก์/RAM สแกนซ้ำซ้อนและขอ Lock ทีละแถว ($N$ Random Accesses) สแกนอ่านแบบบล็อก Vectorized Block ใน 1 Single Pass
ภาระ Transaction Log (WAL) WAL Buffers บวมพองจากการบันทึก Header ยิบย่อย บันทึก WAL แบบกระชับและต่อเนื่อง มีประสิทธิภาพสูงสุด
เวลาประมวลผล (Execution Duration) ระดับหลายชั่วโมง (14 ชั่วโมงในเคส 20 ล้านแถว) ระดับไม่กี่วินาที (8 วินาที เร็วขึ้น 6,000x)
สถาปัตยกรรมระดับลึก: การเปรียบเทียบ Procedural RBAR Loop vs Multi-Core Parallel Vectorized Pipeline Set-Based & Parallel Optimization
❌ แย่: Procedural RBAR Loop (รันทีละแถว CPU ค้าง Core เดียว) ✅ ปัง: Set-Based Vectorized + Parallel Gather Engine (ทุก Cores รันพร้อมกัน) 1. PL/pgSQL Procedural Interpreter Loop • FOR v_rec IN SELECT id, balance FROM loans LOOP ... • บังคับรันแบบ Synchronous ตามลำดับทีละ 1 แถว ห้ามข้ามขั้นตอน ⚠️ บล็อกไม่ให้ Query Planner สร้าง Parallel Execution Plan! 2. วิกฤต Single-Core Exhaustion & Context Switching • CPU Core 1 วิ่ง 100% ตลอดเวลา ในขณะที่ Core 2-8 ว่างเปล่า 0% • เกิด Context Switching สลับข้ามเอนจิน 20,000,000 ครั้ง • ยิงคำสั่ง UPDATE แถวละ 1 คำสั่ง สร้าง WAL Activity มหาศาล 💥 สคริปต์หมุนค้างนานกว่า 14 ชั่วโมง ล้นเกินเวลาเปิดระบบ! ผลกระทบต่อองค์กร: Batch Window Overrun • ข้อมูลปรับปรุงไม่เสร็จก่อน 06:00 น. หน้าสาขาเปิดทำการไม่ได้ • ผู้บริหารเข้าใจผิดว่า "เซิร์ฟเวอร์สเปกต่ำ ต้องซื้อ CPU เพิ่ม" ทั้งที่ความจริงเกิดจากการเขียนโค้ดผิดกระบวนทัศน์ (RBAR) 1. Single Declarative Set-Based Query • UPDATE loans SET penalty = CASE ... END WHERE status = 'ACTIVE'; ⚡ Relational Engine รับทราบทั้งเซตข้อมูลในคำสั่งเดียว • Query Planner ตัดสินใจแตกงานเป็น Parallel Execution Plan • สลายขอบเขตลูปทิ้ง ไร้ Context Switching Overhead 100% 2. Parallel Gather Execution Engine (Multi-Core Power) Worker 1 5M rows Worker 2 5M rows Worker 3 5M rows Worker 4 5M rows Parallel Seq Scan + Vectorized Memory Register Processing ดึงพลัง Multi-core CPU ครบทุก Cores ประมวลผลข้อมูลพร้อมกันในระดับ RAM เวลารวม: จาก 14 ชั่วโมง เหลือเพียง 8 วินาที (เร็วขึ้น 6,300x!) Single Pass Scan บนดิสก์, คืน RAM ทันที, ปลอดภัยต่อระบบงานสาขา 100%
🎙️ สคริปต์คำพูดสำหรับพรีเซนท์บนเวที (Presentation Pitch Script)

"ทุกท่านครับ สคริปต์ Batch ที่รันนาน 14 ชั่วโมง ไม่ได้เกิดจากฐานข้อมูลประมวลผลไม่ไหว แต่เกิดจากการที่เราไปสั่งให้มัน 'วนลูปอ่านทีละแถว' หรือ RBAR ซึ่งไปบล็อกไม่ให้ฐานข้อมูลดึงพลัง Multi-core CPU มาช่วยทำงาน!

วันนี้ด้วย Set-Based Vectorized Processing เราจะลบ LOOP ทิ้งทั้งหมดแล้วรวบเป็น Single Relational Query เพื่อเปิดทางให้ PostgreSQL สั่งใช้ทุก CPU Cores สแกนประมวลผลแบบ Parallel พร้อมกัน ผลลัพธ์คือการตัดเวลาประมวลผลจาก 14 ชั่วโมง เหลือเพียง 8 วินาที ประหยัดเวลาไป 99.9% โดยไม่ต้องซื้อ CPU เพิ่มแม้แต่ตัวเดียวครับ"

🧮 วิดเจ็ตทดลอง: ตัวจำลองผลลัพธ์ RBAR Loop vs Parallel Set-Based Processing

ลองปรับจำนวนแถวข้อมูลในตาราง และเลือกจำนวน Parallel Worker CPU Cores เพื่อเปรียบเทียบเวลาประมวลผลระหว่าง Procedural RBAR Loop (รันทีละแถวบน 1 Core) กับ Set-Based Parallel Optimization

ผลลัพธ์เปรียบเทียบเวลาประมวลผล (Processing Duration Comparison):
• Procedural RBAR Loop: ~14.0 ชั่วโมง (CPU 1 Core แช่แน่น 100%, อีก 7 Cores ว่างเปล่า)
• Set-Based Parallel: 8.0 วินาที (กระจายงานลง 4 Cores พร้อมกันแบบ Vectorized Block)
เร็วขึ้น 6,300 เท่าตัว!
2

💻 ปูพื้นฐานไวยากรณ์ & แบบฝึกหัดไต่ระดับ (Set-Based Logic & Parallel Tuning)

การเปลี่ยนผ่านจากการเขียนโค้ดแบบ Procedural มาเป็น Set-Based ใน PostgreSQL ต้องเริ่มตั้งแต่การเข้าใจความต่างของโครงสร้างคำสั่ง, การปรับจูนพารามิเตอร์ Session ให้เปิดใช้งาน Parallel Workers, และการเขียนคำสั่ง UPDATE ... FROM ร่วมกับ CASE WHEN:

🧱 แกะโครงสร้างไวยากรณ์: การสลาย RBAR Loop เข้าสู่ Set-Based Vectorized Blueprint

ไวยากรณ์หลัก: High-Performance Set-Based & Parallel Optimization Blueprint Day 9 Architecture
-- [สเต็ปที่ 1: ปรับแต่ง Session Parameters เปิดใช้งาน Multi-core Parallel Processing]
SET LOCAL max_parallel_workers_per_gather = 4; -- 🎯 [1] ให้ Planner ดึง Worker มาช่วยได้สูงสุด 4 Cores
SET LOCAL parallel_setup_cost = 1000;          -- 🎯 [2] ลดเกณฑ์ต้นทุนการเริ่มต้น Worker เพื่อกระตุ้น Parallelism
SET LOCAL parallel_tuple_cost = 0.1;           -- 🎯 [3] ต้นทุนการสแกนต่อแถวต่ำลง กระตุ้นให้แบ่งงานหลาย Thread

-- [สเต็ปที่ 2: เขียนคำสั่งเชิงเซต (Set-Based) รวบหลายเงื่อนไขใน 1 Single Pass Scan]
UPDATE accounts a
SET
    penalty_fee = CASE                          -- 🎯 [4] ใช้ CASE WHEN แทน IF-ELSE ในลูป
                    WHEN a.overdue_days > 90 THEN a.balance * 0.15
                    WHEN a.overdue_days > 30 THEN a.balance * 0.08
                    ELSE a.balance * 0.02
                  END,
    credit_class = CASE
                    WHEN a.overdue_days > 90 THEN 'NPL'
                    ELSE 'ACTIVE'
                   END,
    updated_at   = CURRENT_TIMESTAMP
WHERE a.status = 'ACTIVE' AND a.overdue_days > 0; -- 🎯 [5] สแกนอ่านและเขียน Data Pages บนดิสก์เพียง 1 รอบ!
💡 กฎเหล็กสำคัญ: ในระบบฐานข้อมูลเชิงสัมพันธ์ ทุกครั้งที่คุณกำลังจะพิมพ์คีย์เวิร์ด LOOP หรือ CURSOR ให้หยุดคิดเสมอว่าสามารถเขียนด้วย UPDATE ... WHERE, UPDATE ... FROM หรือ CASE WHEN ได้หรือไม่ เพราะการคิดเชิงเซตจะเร็วกว่าลูปหลายร้อยเท่าเสมอ

🏋️‍♂️ แบบฝึกหัดไต่ระดับการเขียนจริง (จากระดับเริ่มต้น ➔ Production Parallel Processing Engine)

🟢 Level 1: การปรับแต่งพารามิเตอร์เพื่อให้ PostgreSQL เปิดใช้งาน Parallel Query ระดับเริ่มต้น
-- [1] ตรวจสอบและตั้งค่าพารามิเตอร์ระดับ Session เพื่อเปิดใช้ Parallel Workers
SET max_parallel_workers_per_gather = 4; -- กำหนดให้ใช้ Parallel Workers ได้สูงสุด 4 Cores ต่อ Query
SET parallel_setup_cost = 1000;          -- ปรับ Cost เกณฑ์การเริ่มใช้งาน Parallel
SET parallel_tuple_cost = 0.1;           -- ปรับ Cost ประมวลผลต่อ Tuple

-- [2] ทดสอบ Query สแกนข้อมูลปริมาณมากเพื่อดู Parallel Execution Plan
EXPLAIN (ANALYZE, BUFFERS)
SELECT category_id, SUM(amount) AS total_amount
FROM orders
GROUP BY category_id;

-- 📤 ตัวอย่างผลลัพธ์ใน Execution Plan:
-- Finalize HashAggregate  (actual time=45.210..45.215 rows=10 loops=1)
--   ->  Gather  (actual time=43.120..44.890 rows=40 loops=1)
--         Workers Planned: 4
--         Workers Launched: 4  <-- 🎯 พิสูจน์ว่าดึงพลัง 4 Cores มาสแกนพร้อมกันจริง!
--         ->  Partial HashAggregate  (actual time=38.100..38.110 rows=10 loops=4)
--               ->  Parallel Seq Scan on orders  (actual time=0.080..25.400 rows=2500000 loops=4)
จุดที่ต้องสังเกต: เมื่อเห็น Node Parallel Seq Scan และ Gather ใน EXPLAIN ANALYZE แสดงว่า PostgreSQL กำลังแบ่งตารางออกเป็นส่วนๆ และสแกนบนหลาย CPU Cores พร้อมกันอย่างเต็มที่
🟡 Level 2: การใช้ UPDATE ... FROM ร่วมกับ CTE เพื่อประมวลผลตรรกะซับซ้อนเชิงเซต ระดับปานกลาง
-- [1] รีแฟกเตอร์ตรรกะการคำนวณแบบหลายเงื่อนไขให้อยู่ในคำสั่ง Set-Based เดียว
WITH computed_metrics AS (
    SELECT
        account_id,
        CASE
            WHEN overdue_days > 90 THEN amount * 0.15
            WHEN overdue_days > 30 THEN amount * 0.08
            ELSE amount * 0.02
        END AS computed_penalty,
        CASE
            WHEN overdue_days > 90 THEN 'NPL'
            ELSE 'ACTIVE'
        END AS new_class
    FROM account_delinquency
    WHERE is_processed = FALSE
)
-- [2] สั่ง UPDATE เชื่อมโยงกับผลลัพธ์จาก CTE ด้วยไวยากรณ์ UPDATE ... FROM
UPDATE accounts a
SET penalty_fee = c.computed_penalty,
    credit_class = c.new_class,
    updated_at   = CURRENT_TIMESTAMP
FROM computed_metrics c
WHERE a.account_id = c.account_id;
จุดที่ต้องสังเกต: การใช้ UPDATE ... FROM ร่วมกับ CTE ช่วยให้เราสามารถ Join ตารางอ้างอิงและอัปเดตหลายคอลัมน์พร้อมกันได้ใน 1 Single Set-Based Scan โดยไม่ต้องเขียนลูปแม้แต่บรรทัดเดียว

🔴 Level 3: Refactoring โค้ด Batch Calculation ระดับ Production (จาก RBAR 14 ชั่วโมง สู่ Set-Based 8 วินาที)

เปรียบเทียบโค้ดเดิมที่เป็น Procedural RBAR Loop ที่บังคับให้ CPU รันอยู่ Core เดียวจนนาน 14 ชั่วโมง กับโค้ดใหม่ที่เป็น Set-Based Vectorized Processing พร้อมเปิดใช้งาน Parallel Optimization:

❌ โค้ดเดิมแบบ Flawed: วนลูป CURSOR ทีละแถว (RBAR 14 ชั่วโมง) Single-Core 12.5% & ช้า 14 ชม.
-- ❌ ห้ามทำในระบบ Production! วนลูป 20 ล้านรอบ ทำให้ช้า 14 ชั่วโมง
CREATE OR REPLACE PROCEDURE pr_process_penalties_flawed()
LANGUAGE plpgsql AS $$
DECLARE
    v_row RECORD;
BEGIN
    -- 🚨 เปิด Cursor วนลูปทีละแถว บล็อกไม่ให้ทำ Parallel Query!
    FOR v_row IN SELECT id, balance, overdue_days FROM loans WHERE status = 'ACTIVE' LOOP
        IF v_row.overdue_days > 60 THEN
            UPDATE loans
            SET penalty = v_row.balance * 0.10, status = 'DEFAULT'
            WHERE id = v_row.id;
        ELSIF v_row.overdue_days > 30 THEN
            UPDATE loans
            SET penalty = v_row.balance * 0.05
            WHERE id = v_row.id;
        END IF;
    END LOOP;
END $$;

-- ผลลัพธ์: เกิด Context Switching 20 ล้านครั้ง CPU ติด Core เดียว นาน 14 ชั่วโมง!
วิเคราะห์ข้อเสีย: การวนลูปในภาษา Procedural บังคับรันแบบ Single-threaded ทีละแถว สิ้นเปลือง Context Switching และทำลายระบบงานบริการหน้าบ้าน
✅ โค้ดใหม่ระดับ Production: Set-Based Vectorized + Parallel Optimization ตอบสนองใน 8 วินาที (เร็วขึ้น 6,300x)
-- [Statement 1] สร้าง Stored Procedure สำหรับประมวลผลเชิงเซต (Set-Based)
CREATE OR REPLACE PROCEDURE pr_process_penalties_production()
LANGUAGE plpgsql
AS $$
DECLARE
    v_rows_affected INT := 0;
    v_start_time    TIMESTAMPTZ := CURRENT_TIMESTAMP;
BEGIN
    -- [บรรทัดที่ 1-2] ตั้งค่า Session ให้ใช้ Multi-Core Parallel Workers ได้เต็มประสิทธิภาพ
    SET LOCAL max_parallel_workers_per_gather = 4;

    -- [บรรทัดที่ 3-18] ยุบการวนลูป 20 ล้านรอบให้เหลือคำสั่ง UPDATE เชิงเซตเพียงคำสั่งเดียว!
    -- ใช้ CASE WHEN คำนวณเงื่อนไขพร้อมกันทุกแถวในสภาวะ Vectorized Block
    WITH target_loans AS (
        UPDATE loans
        SET
            penalty = CASE
                WHEN overdue_days > 60 THEN balance * 0.10
                WHEN overdue_days > 30 THEN balance * 0.05
                ELSE penalty
            END,
            status = CASE
                WHEN overdue_days > 60 THEN 'DEFAULT'
                ELSE status
            END,
            last_calculated_at = CURRENT_TIMESTAMP
        WHERE status = 'ACTIVE' AND overdue_days > 30
        RETURNING id
    )
    SELECT COUNT(*) INTO v_rows_affected FROM target_loans;

    -- [บรรทัดที่ 19-20] บันทึกเวลาประมวลผลจริงและ Commit Transaction
    RAISE NOTICE 'Set-Based Processing Completed: % rows updated in %',
                 v_rows_affected, CURRENT_TIMESTAMP - v_start_time;

    COMMIT;
END $$;

-- [Statement 2] เรียกใช้งานผ่าน CALL (ประมวลผล 20 ล้านแถวเสร็จในเวลาเพียง 8 วินาที!)
CALL pr_process_penalties_production();
วิเคราะห์ข้อได้เปรียบ: สลายลูปทิ้ง 100%, ประมวลผลแบบ Single Pass Scan, ใช้ Multi-core CPU ครบทุก Cores และลดเวลาประมวลผลจาก 14 ชั่วโมงเหลือเพียง 8 วินาที!
3

📝 คลังข้อสอบและแบบประเมินความรู้ (18 ข้อ Multiple Choices พร้อมเฉลยยิบ)

แบบทดสอบวัดระดับความเข้าใจ Day 9: Vectorized Processing, Set-Based Logic & Parallel Query Optimization (RBAR Anti-Pattern, max_parallel_workers_per_gather, UPDATE ... FROM, work_mem) และข้อสอบสะสมทบทวนครบทั้ง Days 1–8 คลิกเลือกคำตอบ ก, ข, ค, หรือ ง เพื่อดูผลลัพธ์และคำอธิบายอย่างละเอียดทันที

📊 สรุปผลการประเมิน Set-Based & Parallel Query (Day 9) 0 / 18
ความคืบหน้า ตอบแล้ว 0 จาก 18 ข้อ

🎉 ยินดีด้วยครับ! คุณได้ผ่านบทเรียนใน Phase 2 (Deep Dive into the 4 Pillars - Days 5–9) เรียบร้อยแล้ว

บทเรียน Day 9: Vectorized Processing, Set-Based Logic & Parallel Query Optimization ได้ปลูกฝังการเปลี่ยน Mindset ทางสถาปัตยกรรมจากการเขียนโค้ดวนลูปทีละแถว (Procedural RBAR) มาเป็นการคิดแบบ Set-Based Vectorized Processing ร่วมกับการเปิดใช้งาน Parallel Query Execution ซึ่งช่วยขจัดภาระ Context Switching และดึงพลังของ Multi-core CPU มาช่วยประมวลผลได้อย่างเต็มศักยภาพ

🚩 ก้าวต่อไปสู่ Day 10 (Phase 2 Synthesis & Capstone Architectural Review):

ในวันถัดไป เราจะไปสรุปรวบยอดเสาหลักทั้ง 4 ต้น (UDFs, Procedures, Triggers, Materialized Views) พร้อมทำแบบจำลองสถาปัตยกรรมระบบ E-Commerce / Financial Engine ฉบับสมบูรณ์ ก่อนก้าวเข้าสู่ Phase 3: DevOps, Maintainability & Architectural Boundaries ครับ!