← Back to PostgreSQL course
PHASE 2: DEEP DIVE INTO THE 4 PILLARS PILLAR 3: TRIGGERS 🛡️

Day 7: Pillar 3 - Event Triggers & Audit Logging Mechanics (กลไกการทำงานของ Triggers และ Trigger Functions, โครงสร้างพิเศษ OLD / NEW, ความแตกต่างระหว่าง BEFORE vs AFTER และการสร้าง Real-Time Automatic Audit Logging Engine)

สร้างระบบความปลอดภัยและการตรวจสอบย้อนหลังที่ไม่มีวันถูก Bypass! เจาะลึกการสร้าง Trigger และ Trigger Function ระดับ Database Engine, เข้าใจความต่างที่ส่งผลถึงชีวิตของ BEFORE vs AFTER, ไขรหัสตัวแปรมหัศจรรย์ OLD และ NEW, พร้อมสร้าง Generic JSONB Audit Logging Engine ที่บันทึกประวัติการเปลี่ยนแปลงของทุกตารางลงตารางกลางเพียงตารางเดียว

📍 ตำแหน่งปัจจุบันของคุณบน Roadmap (18-Day Masterclass) Phase 2: Pillar 3 of 4
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 & Audit
➔
Day 8: Pillar 4 - Pre-computation
เปิดบท

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

เสาหลักต้นที่สามไม่ได้อยู่ที่ความเร็ว แต่อยู่ที่ความปลอดภัย — ใครแตะข้อมูลแถวไหน เปลี่ยนจากค่าอะไรเป็นอะไร และไม่มีใครแอบบายพาสกล้องวงจรปิดนี้ได้

Event Triggers

โค้ดที่ฐานข้อมูลเรียกให้ทำงานเอง เมื่อเกิดเหตุการณ์กับตาราง เช่น INSERT, UPDATE, DELETE เลือกได้ว่าจะให้ทำงานก่อนคำสั่งนั้น (BEFORE) หรือหลังคำสั่งนั้นจบ (AFTER)

Audit Logging

การบันทึกประวัติว่าใครมาทำอะไรกับข้อมูลแถวไหน เปลี่ยนจากค่าเก่าเป็นค่าอะไร เมื่อไหร่

โดยรวมวันนี้คือการผูก Trigger ไว้กับตาราง แล้วให้มันแปลงค่าเก่า/ใหม่เป็น JSONB บันทึกลงตาราง Audit Log ทุกครั้งที่มีการแก้ไข ไม่ว่าจะแก้ผ่านแอป สคริปต์ หรือเข้าไปแตะในฐานข้อมูลตรงๆ

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

เหมือนกล้องวงจรปิดพร้อมยามหน้าประตู ยามตรวจกระเป๋าก่อนเข้า (BEFORE) เพื่อไม่ให้นำของผิดกฎหมายเข้ามา และเมื่อเดินผ่านประตูออกไป กล้องบันทึกภาพทันที (AFTER) ว่าใครทำอะไรไปบ้าง — ไม่มีทางเดินเลี่ยงกล้องได้

ทำให้ไม่ว่าจะแก้ข้อมูลผ่านแอป สคริปต์ หรือแก้ในฐานข้อมูลตรงๆ ก็ถูกบันทึก Audit Trail ครบทุกครั้ง

1

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

ในระบบงานระดับ Enterprise หัวใจที่สำคัญที่สุดไม่แพ้เรื่องความเร็วคือ "ความปลอดภัยและการตรวจสอบย้อนหลังได้ (Auditability & Compliance)" ทีมพัฒนามักทำผิดพลาดครั้งใหญ่ด้วยการเขียนโค้ดบันทึก Audit Log ไว้บน Application Server (เช่น Node.js / Python / C#) ซึ่งก่อให้เกิดช่องโหว่ร้ายแรง เพราะหากมีใครใช้สิทธิ DBA หรือแอบยิง SQL ผ่านเครื่องมืออย่าง DBeaver/pgAdmin ข้อมูลจะถูกแก้ไขเงียบๆ โดย ไม่มี Audit Log ใดๆ เกิดขึ้นเลย!

🚨 วิกฤตสินเชื่อลึกลับ: วงเงินถูกแอบแก้ผ่านหลังบ้าน แต่ใน Log ฝั่ง App ว่างเปล่า!

สถาบันการเงินแห่งหนึ่งตรวจพบว่ามีบัญชีลูกค้ารายใหญ่ถูกขยายวงเงินสินเชื่อ (credit_limit) จาก 1,000,000 บาท กลายเป็น 50,000,000 บาท ทีม Security ตรวจไฟล์ Log ของ Application ปรากฏว่า "ไม่มีประวัติการทำงานใดๆ เลย" เพราะคนร้ายใช้สิทธิ DB Admin เชื่อมต่อตรงเข้ามาแก้ข้อมูลในตาราง customers:

0 Records
App Audit Log ว่างเปล่า (ถูก Bypass 100%)
Compliance Fail
ผิดกฎบัตรธนาคารแห่งประเทศไทย และ PCI-DSS
Accidental Abort
เขียน BEFORE Trigger ผิดท่าจนคำสั่งหลักพังทลาย

ทางออกเชิงสถาปัตยกรรมหนึ่งเดียวที่ไม่มีวันถูก Bypass คือการย้ายกลไกตรวจสอบและบันทึกประวัติมาไว้ที่ In-Database Event Triggers ที่ทำงานอยู่ระดับรากฐานของ PostgreSQL Engine ครับ!

💂‍♂️ อุปมาอุปไมย: ยามเฝ้าประตูห้องนิรภัย (BEFORE vs AFTER) และกล้องวงจรปิดที่ไม่เคยหลับ

🔍 BEFORE Trigger (ยามตรวจกระเป๋าก่อนเข้าประตู)

เปรียบเสมือนยามที่ยืนตรวจค้นหน้าประตู ก่อนที่คุณจะก้าวขาเข้าห้องนิรภัย

  • หน้าที่หลัก: ปรับแต่งหรือตรวจเช็คของ เช่น ยามประทับตราเวลา (NEW.updated_at := NOW()) หรือริบอาวุธ
  • กับดักอันตราย: ถ้ายามสั่ง RETURN NULL; ประตูจะถูกปิดใส่หน้าทันที! คำสั่ง INSERT/UPDATE จะถูกยกเลิก (Cancelled) ราวกับไม่เคยเกิดขึ้น
  • ข้อควรระวัง: ห้ามใช้บันทึก Audit Log เพราะถ้าบันทึกแล้วแต่ตอนหลังข้อมูลลงดิสก์ไม่ผ่าน Log จะเพี้ยน!
📹 AFTER Trigger (กล้องวงจรปิดที่บันทึกหลังงานเสร็จ)

เปรียบเสมือนกล้องวงจรปิดที่บันทึกเทป หลังจากที่คุณวางสิ่งของลงในตู้เซฟและเซ็นรับเรียบร้อยแล้ว

  • หน้าที่หลัก: สังเกตการณ์และบันทึกประวัติ (Audit Logging / Notification)
  • ความปลอดภัย: ไม่สามารถแก้ไขค่าของที่อยู่ในเซฟได้แล้ว (แก้ NEW ไม่ได้) และการ RETURN NULL; ใน AFTER จะ ไม่ยกเลิก คำสั่งหลัก
  • การันตีความจริง: บันทึกเฉพาะข้อมูลที่ผ่านการลงดิสก์จริง 100% ไม่มีทางถูก Bypass แม้แอดมินจะแอบยิง SQL หลังบ้าน!

⚖️ การเปรียบเทียบเชิงสถาปัตยกรรม: BEFORE Trigger vs. AFTER Trigger ใน PostgreSQL

มิติเปรียบเทียบสถาปัตยกรรม BEFORE Trigger (Gatekeeper) AFTER Trigger (Auditor)
จังหวะการทำงาน (Execution Timing) ก่อนข้อมูลถูกบันทึก/แก้ไขลง Data Page จริง หลังจากข้อมูลถูกเขียนลง Data Page ใน Transaction แล้ว
ความสามารถในการแก้ไขตัวแปร NEW ✅ แก้ไขได้ (เช่น NEW.col := value;) ❌ แก้ไขไม่ได้ (ตัวแปร NEW เป็น Read-only)
ผลลัพธ์เมื่อสั่ง RETURN NULL; 🚨 ยกเลิก (Cancel) คำสั่งหลักทันที ข้อมูลแถวนั้นจะไม่ถูกบันทึก ✅ ปลอดภัย คำสั่งหลักยังคงทำงานสำเร็จตามปกติ
งานที่เหมาะสม (Sweet Spot) Data Sanitization, Auto Timestamp, Business Rule Validation Audit Logging, Cascade Notification, External Event Triggering
ความเสี่ยงด้าน Performance ส่งผลโดยตรงต่อ Execution Plan ก่อนเขียนดิสก์ เพิ่มภาระ I/O เขียนตาราง Audit เพิ่มเติมใน Transaction เดียวกัน
สถาปัตยกรรมระดับลึก: วงจรชีวิตของคำสั่ง SQL และจุดดักจับ BEFORE / AFTER Triggers Pillar 3 Event Pipeline
1. SQL Request UPDATE customers (มาจาก App หรือ DBeaver) 2. BEFORE Phase • เข้าถึง OLD และ NEW • ดัดแปลงค่า NEW ได้ ⚠️️ หาก RETURN NULL = ยกเลิก! ✅ ต้อง RETURN NEW; 3. Storage Mutation • เขียนข้อมูลลง Data Page • บันทึก WAL Buffer สถานะ: Dirty Page ใน RAM 4. AFTER Phase • บันทึก Audit Log • แปลง to_jsonb(OLD/NEW) • Non-Bypassable 100% ✅ ปลอดภัย: RETURN NULL; 💡 กฎเหล็กของตัวแปรพิเศษ OLD และ NEW ในแต่ละเหตุการณ์ (TG_OP Matrix): • TG_OP = 'INSERT' : มีเฉพาะ NEW (ค่าที่กำลังเพิ่ม) ส่วน OLD จะเป็น NULL • TG_OP = 'UPDATE' : มีทั้ง OLD (ค่าเดิมก่อนแก้) และ NEW (ค่าใหม่หลังแก้) สมบูรณ์พร้อมเปรียบเทียบ Diff • TG_OP = 'DELETE' : มีเฉพาะ OLD (ค่าเดิมก่อนโดนลบ) ส่วน NEW จะเป็น NULL
🎙️ สคริปต์คำพูดสำหรับพรีเซนท์บนเวที (Presentation Pitch Script)

"ทุกท่านครับ Audit Log ที่เขียนบน App Code สามารถถูก Bypass ได้ง่ายๆ เพียงแค่ใครสักคนใช้สิทธิ Admin ยิง SQL ตรงเข้าฐานข้อมูล!

ด้วย Pillar 3 Architecture เราจะสร้าง In-Database Non-Bypassable Audit Engine ที่ใช้ AFTER Trigger ร่วมกับกลไก to_jsonb(OLD) และ to_jsonb(NEW) บันทึกประวัติค่าเดิมและค่าใหม่ลงตาราง Audit กลางให้อัตโนมัติ 100% ดักจับได้ทุกทิศทางโดยไม่มีทางถูก Bypass และไม่กระทบต่อคำสั่งหลักหน้าบ้านครับ"

🧮 วิดเจ็ตทดลอง: ตัวจำลองพฤติกรรม BEFORE vs AFTER Trigger (Phase & Variable Simulator)

ลองเลือกคำสั่ง (Operation), เลือกระยะ Trigger (Phase), และเลือกค่าที่ฟังก์ชันส่งคืน (Return Value) เพื่อดูว่า PostgreSQL จะตอบสนองอย่างไร และคำสั่งหลักจะรอดหรือพัง

ผลลัพธ์การทำงานของระบบ (Database Outcome):
• ตัวแปรที่ใช้งานได้: OLD (มีค่าเดิม) และ NEW (มีค่าใหม่)
• การทำงานของคำสั่งหลัก: สำเร็จสมบูรณ์ (AFTER Trigger ไม่สนค่า RETURN NULL)
✅ คำสั่งสำเร็จ 100%
2

💻 ปูพื้นฐานไวยากรณ์ & แบบฝึกหัดไต่ระดับ (Trigger Mechanics & Hands-on)

การสร้าง Trigger ใน PostgreSQL ต้องทำ 2 สเต็ปเสมอ: สเต็ปที่ 1 สร้าง Trigger Function ที่มี RETURNS TRIGGER และสเต็ปที่ 2 ผูก Trigger เข้ากับตารางด้วยคำสั่ง CREATE TRIGGER

🧱 แกะโครงสร้างไวยากรณ์ 2 สเต็ป: แม่แบบการสร้าง Trigger ที่ถูกต้อง

ไวยากรณ์หลัก: High-Performance Trigger Blueprint Pillar 3 Architecture
-- [สเต็ปที่ 1: สร้าง Trigger Function]
CREATE OR REPLACE FUNCTION fn_trg_sample_audit()
RETURNS TRIGGER -- 🎯 [1] ต้องระบุชนิดผลลัพธ์เป็น TRIGGER เท่านั้น
LANGUAGE plpgsql
AS $$
BEGIN
    -- [2] ตัวแปรสภาวะแวดล้อม TG_OP บอกว่ากำลังทำอะไร ('INSERT', 'UPDATE', 'DELETE')
    IF (TG_OP = 'UPDATE') THEN
        -- [3] เข้าถึงเรกคอร์ด OLD (ค่าเดิม) และ NEW (ค่าใหม่)
        RAISE NOTICE 'แก้ไขแถว ID % จากสถานะ % เป็น %', OLD.id, OLD.status, NEW.status;
    END IF;

    -- [4] กฎเหล็กของการคืนค่า:
    -- สำหรับ BEFORE: ต้อง RETURN NEW; (ถ้า RETURN NULL คำสั่งหลักจะถูกยกเลิก!)
    -- สำหรับ AFTER: สามารถ RETURN NULL; ได้อย่างปลอดภัย
    RETURN NEW;
END $$;

-- [สเต็ปที่ 2: ผูก Trigger เข้ากับตารางเป้าหมาย]
CREATE TRIGGER trg_sample_audit
AFTER UPDATE ON customers -- 🎯 [5] เลือกระยะ (BEFORE/AFTER), เหตุการณ์ (INSERT/UPDATE/DELETE), และตาราง
FOR EACH ROW              -- 🎯 [6] บังคับให้รันย่อยทีละแถว (Row-level Trigger) เพื่อให้มี OLD/NEW
EXECUTE FUNCTION fn_trg_sample_audit(); -- [7] ชี้ไปยังฟังก์ชันในสเต็ปที่ 1
💡 กฎเหล็กสำคัญ: ใน PostgreSQL Trigger Function ห้ามรับพารามิเตอร์ปกติในวงเล็บ แต่ข้อมูลทั้งหมดจะถูกส่งผ่านตัวแปรพิเศษอัตโนมัติ เช่น OLD, NEW, TG_OP, และ TG_TABLE_NAME

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

🟢 Level 1: การใช้ BEFORE Trigger อัปเดตคอลัมน์ updated_at อัตโนมัติ ระดับเริ่มต้น
-- [1] สร้างฟังก์ชันปรับแต่ง timestamp อัตโนมัติก่อนบันทึกลงดิสก์
CREATE OR REPLACE FUNCTION fn_trg_set_timestamp()
RETURNS TRIGGER 
LANGUAGE plpgsql 
AS $$
BEGIN
    -- 🎯 แก้ไขฟิลด์ updated_at ในเรกคอร์ด NEW โดยตรงก่อนเขียนลงดิสก์
    NEW.updated_at := CURRENT_TIMESTAMP;

    -- 🎯 ใน BEFORE Trigger ต้องส่งคืน NEW เพื่อให้ข้อมูลถูกนำไปเซฟลงตาราง
    RETURN NEW;
END $$;

-- [2] ผูก Trigger เข้ากับตาราง customers
CREATE TRIGGER trg_customers_set_timestamp
BEFORE UPDATE ON customers
FOR EACH ROW
EXECUTE FUNCTION fn_trg_set_timestamp();

-- 📤 ทดสอบรันคำสั่งแก้ไขข้อมูล:
-- UPDATE customers SET name = 'สมศักดิ์' WHERE id = 101;
-- คอลัมน์ updated_at จะถูกปรับเวลาเป็นวินาทีปัจจุบันให้อัตโนมัติทันที!
จุดที่ต้องสังเกต: เราเลือกใช้ BEFORE UPDATE เพราะเราต้องการแทรกแซงและแก้ไขค่าในเรกคอร์ด NEW ก่อนที่มันจะถูกเขียนลง Data Page บนดิสก์
🟡 Level 2: การใช้ BEFORE Trigger เพื่อ Validate และยับยั้งข้อมูลที่ไม่ถูกต้อง ระดับปานกลาง
-- [1] ฟังก์ชันตรวจสอบความปลอดภัย: ห้ามเพิ่มวงเงินเกิน 50% ในครั้งเดียว
CREATE OR REPLACE FUNCTION fn_trg_validate_credit_limit()
RETURNS TRIGGER 
LANGUAGE plpgsql 
AS $$
BEGIN
    -- 🎯 เทียบค่าใหม่ (NEW.credit_limit) กับค่าเดิม (OLD.credit_limit)
    IF (NEW.credit_limit > OLD.credit_limit * 1.5) THEN
        -- พ่น EXCEPTION เพื่อ Abort และ Rollback Transaction ทันที
        RAISE EXCEPTION 'ไม่อนุญาตให้เพิ่มวงเงินเกิน 50%% ในครั้งเดียว (วงเงินเดิม: %, วงเงินใหม่: %)',
                        OLD.credit_limit, NEW.credit_limit;
    END IF;

    -- ถ้าผ่านเงื่อนไข ให้คืนค่า NEW ดำเนินการต่อตามปกติ
    RETURN NEW;
END $$;

-- [2] ผูก Trigger แบบเจาะจงเฉพาะคอลัมน์ (UPDATE OF column_name) เพื่อลด Overhead
CREATE TRIGGER trg_validate_credit_limit
BEFORE UPDATE OF credit_limit ON customers -- 🎯 ทำงานเฉพาะเมื่อมีการแก้ credit_limit เท่านั้น
FOR EACH ROW
EXECUTE FUNCTION fn_trg_validate_credit_limit();
จุดที่ต้องสังเกต: การระบุ BEFORE UPDATE OF credit_limit ช่วยเพิ่มความเร็ว เพราะหากคำสั่งแก้เฉพาะชื่อหรือที่อยู่ Trigger ตัวนี้จะไม่ถูกปลุกขึ้นมาทำงานเลย

🔴 Level 3: Refactoring โค้ด Audit Logging ระดับ Production (Generic JSONB Audit Engine)

เปรียบเทียบโค้ดเดิมที่ใช้ BEFORE Trigger แล้วใส่ RETURN NULL; ผิดพลาด จนคำสั่งหลักโดน Cancel ทิ้ง กับโค้ดใหม่ที่เป็น Generic JSONB Audit Engine สมบูรณ์แบบ:

❌ โค้ดเดิมแบบ Flawed: BEFORE Trigger Audit + RETURN NULL คำสั่งหลักโดน Cancel ทิ้ง 100%
-- ❌ หายนะทางสถาปัตยกรรม: ใช้ BEFORE Trigger ทำ Audit Log
-- แล้วส่งคืนค่า RETURN NULL;
CREATE OR REPLACE FUNCTION fn_trg_audit_flawed() 
RETURNS TRIGGER 
LANGUAGE plpgsql 
AS $$
BEGIN
    -- บันทึกลงตาราง Log
    INSERT INTO audit_logs(table_name, old_data) 
    VALUES (TG_TABLE_NAME, OLD.status);

    -- 🚨 พังพินาศทันที! การส่ง RETURN NULL ใน BEFORE Trigger
    -- จะสั่งให้ PostgreSQL ยกเลิกคำสั่ง UPDATE หลักทิ้งทันที!
    RETURN NULL; 
END $$;

CREATE TRIGGER trg_audit_bad
BEFORE UPDATE ON customers
FOR EACH ROW
EXECUTE FUNCTION fn_trg_audit_flawed();
-- ลูกค้ากดอัปเดตข้อมูล แต่ข้อมูลในตารางไม่เคยเปลี่ยนเลย!
วิเคราะห์ข้อเสีย: ใน BEFORE Trigger คำสั่ง RETURN NULL; ถือเป็นคำสั่ง Cancel Operation ทำให้ตารางหลักไม่ถูกอัปเดตเลยแม้แต่แถวเดียว
✅ โค้ดใหม่ระดับ Production: Generic JSONB Engine + AFTER Trigger Non-Bypassable 100% & ปลอดภัย
-- [Statement 1] สร้างตาราง Audit Log กลางสำหรับรองรับประวัติทุกตารางในระบบ
CREATE TABLE IF NOT EXISTS audit_logs (
    audit_id     BIGSERIAL PRIMARY KEY,
    table_name   VARCHAR(100) NOT NULL,
    operation    VARCHAR(10)  NOT NULL, -- 'INSERT', 'UPDATE', 'DELETE'
    old_data     JSONB,                -- บันทึกโครงสร้างเก่าเป็น JSONB
    new_data     JSONB,                -- บันทึกโครงสร้างใหม่เป็น JSONB
    changed_by   VARCHAR(100) DEFAULT CURRENT_USER,
    changed_at   TIMESTAMPTZ  DEFAULT CURRENT_TIMESTAMP
);

-- [Statement 2] สร้าง Generic Trigger Function แปลงเรกคอร์ดเป็น JSONB
CREATE OR REPLACE FUNCTION fn_trg_generic_audit_engine()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
    -- [บรรทัดที่ 1-2] ประกาศตัวแปรเก็บโครงสร้าง JSONB ค่าเดิมและค่าใหม่
    v_old_jsonb JSONB := NULL;
    v_new_jsonb JSONB := NULL;
BEGIN
    -- [บรรทัดที่ 3-7] ตรวจสอบเหตุการณ์ TG_OP เพื่อแปลงเรกคอร์ดเป็น JSONB
    IF (TG_OP = 'UPDATE') THEN
        v_old_jsonb := to_jsonb(OLD); -- แปลงเรกคอร์ด OLD เป็น JSONB
        v_new_jsonb := to_jsonb(NEW); -- แปลงเรกคอร์ด NEW เป็น JSONB
    ELSIF (TG_OP = 'DELETE') THEN
        v_old_jsonb := to_jsonb(OLD);
    ELSIF (TG_OP = 'INSERT') THEN
        v_new_jsonb := to_jsonb(NEW);
    END IF;

    -- [บรรทัดที่ 8-16] บันทึกลงตาราง audit_logs กลาง
    INSERT INTO audit_logs (
        table_name,
        operation,
        old_data,
        new_data
    ) VALUES (
        TG_TABLE_NAME, -- ชื่อนามตารางอัตโนมัติ
        TG_OP,         -- ชนิดการทำงาน ('INSERT'/'UPDATE'/'DELETE')
        v_old_jsonb,
        v_new_jsonb
    );

    -- 🎯 [บรรทัดที่ 17] สำหรับ AFTER Trigger คืนค่า NULL ได้ ปลอดภัย ไม่กระทบคำสั่งหลัก!
    RETURN NULL;
END $$;

-- [Statement 3] ผูก Trigger เข้ากับตารางสำคัญแบบ AFTER FOR EACH ROW
CREATE TRIGGER trg_audit_customers_change
AFTER INSERT OR UPDATE OR DELETE ON customers
FOR EACH ROW
EXECUTE FUNCTION fn_trg_generic_audit_engine();
วิเคราะห์ข้อได้เปรียบ: ฟังก์ชันเดียวผูกได้กับทุกตารางในระบบ, ใช้ to_jsonb() รองรับทุกโครงสร้างคอลัมน์, ทำงานในระยะ AFTER การันตีว่าคำสั่งหลักทำงานสำเร็จและไม่โดน Cancel แน่นอน
3

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

แบบทดสอบวัดระดับความเข้าใจ Pillar 3: Event Triggers & Audit Logging Mechanics (BEFORE vs AFTER, OLD/NEW, TG_OP, to_jsonb) และข้อสอบสะสมทบทวนครบทั้ง Day 1–6 คลิกเลือกคำตอบ ก, ข, ค, หรือ ง เพื่อดูผลลัพธ์และคำอธิบายอย่างละเอียดทันที

📊 สรุปผลการประเมิน Pillar 3: Triggers (Day 7) 0 / 18
ความคืบหน้า ตอบแล้ว 0 จาก 18 ข้อ

🎯 สรุปหมุดเชื่อมโยง & ก้าวต่อไปสู่ Day 8

บทเรียน Day 7: Pillar 3 - Event Triggers & Audit Logging Mechanics แสดงให้เห็นถึงการสร้างระบบรักษาความปลอดภัยและการตรวจสอบย้อนหลังที่ไม่มีทางถูก Bypass ด้วย In-Database Triggers การเลือกใช้ AFTER Trigger ร่วมกับตัวแปร OLD, NEW, TG_OP และเทคนิคการแปลงเป็น JSONB ช่วยให้เราบันทึกประวัติการเปลี่ยนแปลงข้อมูลของทั้งระบบได้อย่างมีประสิทธิภาพสูงสุด

🚩 ก้าวต่อไปสู่ Day 8:

Pillar 4 - Pre-computation with Materialized Views & pg_cron: ในวันถัดไป เราจะไปเจาะลึกเสาหลักสุดท้ายของ Phase 2 นั่นคือ Pre-computation & Background Scheduling Engine ด้วย Materialized Views (REFRESH MATERIALIZED VIEW CONCURRENTLY) และการตั้งเวลาทำงานอัตโนมัติด้วย pg_cron เพื่อยกเลิกภาระการคำนวณแดชบอร์ดหนักๆ ในเวลา Peak ครับ!