P
PipeCraft

Roadmap Progress

ข้อมูลเบื้องต้นเกี่ยวกับวิศวกรรมข้อมูล (Introduction to Data Engineering) Python สำหรับ Data Engineering พื้นฐาน Linux & CLI สำหรับวิศวกรข้อมูล Git สำหรับระบบงานข้อมูลและทีมพัฒนา ระบบเครือข่ายและระบบประมวลผลแบบกระจายศูนย์ (Networking & Distributed Systems) SQL ขั้นสูง (Window Functions & Optimization) ฐานข้อมูลไม่ใช่เชิงสัมพันธ์ (NoSQL Databases & Modern Stores) การออกแบบโครงสร้างข้อมูล (Star/Snowflake & SCD) โครงสร้างพื้นฐานเครือข่ายระบบคลาวด์ (Cloud-based Networking) สถาปัตยกรรม Data Lakehouse (Apache Iceberg & MinIO) Local-first Data Engineering ด้วย DuckDB โครงสร้างพื้นฐานในรูปแบบโค้ด (IaC ด้วย Terraform) การแปลงข้อมูลระดับโปรด้วย dbt-core ข้อตกลงร่วมด้านข้อมูล (Data Contracts) การบริการและการส่งต่อข้อมูลวิเคราะห์ (Data Serving & Reverse ETL) การประมวลผลข้อมูลขนาดใหญ่แบบกระจายด้วย Apache Spark ระบบจัดการเวิร์กโฟลว์ข้อมูล (Airflow, Dagster & Prefect) การประมวลผลข้อมูลแบบเรียลไทม์ด้วย Kafka & Redpanda การจัดการคอนเทนเนอร์และคลัสเตอร์ (Containers & Kubernetes) ระบบ CI/CD และการเฝ้าระวังคุณภาพข้อมูล (CI/CD, Monitoring & Testing) ระบบความปลอดภัยและการควบคุมข้อมูล (Security, Governance & Privacy) ระบบปฏิบัติการและประมวลผลโมเดล (Machine Learning & MLOps)
บทที่ 4: การนำเข้าและการแปลงรูปข้อมูล (Data Ingestion & Transformation)

การแปลงข้อมูลระดับโปรด้วย dbt-core

4.1 กระบวนการเขียนตารางข้อมูลวิเคราะห์ด้วย dbt-core

dbt (Data Build Tool) ทำหน้าที่เป็นแกนหลักในส่วนการแปลงข้อมูล (Transform) ของท่อส่งข้อมูล โดยทำหน้าที่คอมไพล์ความสัมพันธ์โมเดล (DAG) และนำโครงสร้างซอฟต์แวร์ เช่น ยูนิตเทส การควบคุมเวอร์ชัน และการตรวจสอบสกีมา มาจัดการในชั้นข้อมูล SQL

1. วิศวกรรมข้อมูลวิเคราะห์และการคอมไพล์กราฟแสดงความสัมพันธ์ (DAG Compilation)

ทฤษฎีและกลไกการทำงาน (How it works): dbt-core ทำงานในฐานะคลังเก็บโค้ด SQL ที่จัดรูปเป็นแบบระบุปลายทาง (Declarative) โดยใช้คำสั่งระบุความสัมพันธ์เป้าหมายด้วยคำฟังก์ชัน `ref()` และ `source()`. เมื่อสั่งรันคำสั่ง ตัว dbt จะสแกนโฟลเดอร์ไฟล์โมเดลทั้งหมดเพื่อทำการสร้างและคอมไพล์ **Directed Acyclic Graph (DAG)** เพื่อแสดงความสัมพันธ์การไหลของตารางข้อมูล ก่อนจะสร้างคำสั่ง DDL (Data Definition Language) ยิงไปประมวลผลบนคลังข้อมูลปลายทางโดยตรงตามลำดับชั้นความสัมพันธ์ที่ถูกต้อง

-- Defining dependencies inside dbt models using Jinja functions
-- models/marts/fct_weekly_sales.sql
WITH daily_orders AS (
    SELECT * FROM {{ ref('stg_orders') }} -- Tells dbt compiler to compute stg_orders first
),
customer_details AS (
    SELECT * FROM {{ ref('dim_customers') }}
)
SELECT ...
                    

Use Case ในชีวิตจริง (Real-world Scenario): คลังข้อมูลของบริษัทประกันภัยที่มีโมดูล SQL เชื่อมต่อกันกว่า 500 ตาราง การเปลี่ยนโครงสร้างฟิลด์ในตารางฐานล่าง dbt ช่วยระบุความเกี่ยวข้องข้ามสายและไล่แปลงค่าตารางในชั้นถัดไปตามลำดับโดยไม่สร้างความขัดแย้งเชิงโครงสร้าง

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การเกิดโครงสร้างความสัมพันธ์แบบลูปวงกลม (Circular Dependency เช่น ตาราง A เรียก B และ B เรียก A) ซึ่งจะทำให้คอมไพเลอร์ของ dbt รันไม่ผ่าน แก้ไขโดยถอดถอนตรรกะประมวลผลย่อยออกมาสร้างตารางสะพานตัวกลาง (Bridge model) เพื่อตัดการเชื่อมโยงวงกลม

2. ยุทธวิธีการบันทึกตารางทางกายภาพ (Materialization Strategies)

ทฤษฎีและกลไกการทำงาน (How it works): dbt สนับสนุนการทำ Materializations ซึ่งคือการเลือกแปลงรหัส SQL ไปจัดเก็บเป็นสเตทจริงบนฐานข้อมูล 4 รูปแบบ: 1. **View**: สร้างวิวคิวรีจำลองขึ้นมา ไม่เปลืองพื้นที่ดิสก์ แต่จะคำนวณใหม่ทุกครั้งที่มีการเรียกสืบค้นข้อมูล 2. **Table**: บิวด์สร้างตารางดิสก์จริงเต็มรูปแบบ โดยจะรันคำสั่งลบและบิวด์ตารางใหม่ทั้งหมดในทุกครั้งที่รัน (Drop and Recreate) ปลอดภัยต่อข้อมูลนิ่งแต่กินเวลาการประมวลผลสูง 3. **Ephemeral**: แปลงโมเดลเป็นโครงสร้างย่อย (Common Table Expression - CTE) ไปสอดแทรกในโมเดลถัดไปแทนการเขียนลงฐานข้อมูล 4. **Incremental**: ประมวลผลและแทรกเฉพาะข้อมูลพาร์ทิชันแถวใหม่ที่เพิ่มขึ้น ช่วยเซฟเวลาและงบประมาณการดึงข้อมูล OLAP คลาวด์ได้สูงสุด

# Configuration inside dbt model block defining materialization type
# models/staging/stg_orders.sql
{{ config(
    materialized='table',
    indexes=[{'columns': ['order_id'], 'type': 'btree'}]
) }}
                    

Use Case ในชีวิตจริง (Real-world Scenario): การนำข้อมูลดิบล็อกการกดเว็บระดับหลายกิกะไบต์มาทรานฟอร์ม วิศวกรข้อมูลเลือกใช้โมเดลประเภท `incremental` รันงานอัปเดตสรุปพฤติกรรมลูกค้าทุกชั่วโมง เพื่อเลี่ยงภาระค่าประมวลผลจากการที่ต้องรันสแกนข้อมูลเดิมซ้ำไปมา

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การรันโมเดลแบบ Table บนฐานข้อมูลขนาดใหญ่เป็นร้อยล้านแถวทำให้เกิดความล่าช้าและเปลืองค่าคอมพิวต์คลาวด์ แก้ไขโดยปรับเปลี่ยนสเปคโมเดลเหล่านั้นให้มาใช้กลไก `incremental` โดยกรองดักเฉพาะฟิลด์แก้ไขล่าสุด

3. กลไกยุทธวิธีประมวลผลส่วนต่าง (Incremental Models - Append-Only & Merge)

ทฤษฎีและกลไกการทำงาน (How it works): ในโหมดการรันแบบส่วนต่าง dbt จะประยุกต์ใช้งาน 2 รูปแบบย่อยเพื่อซิงก์ข้อมูลเข้าตารางเป้าหมาย: - **Append-only**: นำระเบียนข้อมูลใหม่ไปเขียนต่อท้ายตารางโดยตรงอย่างรวดเร็ว เหมาะสำหรับตารางล็อกเหตุการณ์ที่ไม่เคยมีการแก้ไขย้อนหลัง - **Merge**: นำคีย์หลัก (Unique Key) ของข้อมูลชุดใหม่มาเทียบชนกับตารางหลัก หากคีย์ตรงกันจะสั่งอัปเดตฟิลด์ (Update) หากไม่เจอจะสั่งแทรกแถวใหม่ (Insert) ช่วยรักษาความสะอาดไม่ให้เกิดรายการแถวซ้ำซ้อน

-- Incremental configuration using merge strategy
-- models/marts/fct_orders.sql
{{ config(
    materialized='incremental',
    unique_key='order_id',
    incremental_strategy='merge'
) }}

SELECT * FROM {{ ref('stg_orders') }}
{% if is_incremental() %}
  -- Filter records updated within the last lookback window
  WHERE order_updated_at >= (SELECT MAX(order_updated_at) FROM {{ this }})
{% endif %}
                    

Use Case ในชีวิตจริง (Real-world Scenario): ตารางประวัติยอดขายสินค้าของระบบอีคอมเมิร์ซที่สถานะคำสั่งซื้อสามารถเปลี่ยนจาก "กำลังจัดส่ง" ไปเป็น "จัดส่งสำเร็จ" การใช้ Merge Strategy ในโหมด Incremental ช่วยอัปเดตสถานะของออร์เดอร์เดิมได้อย่างถูกต้องครบถ้วน

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ความผิดพลาดของผลรวมยอดในตารางปลายทางหากมีข้อมูลเก่าที่ยกเลิกไปแล้วไม่ถูกตามไปล้างทิ้ง แก้ไขโดยเปิดใช้งานคำสั่งรันแบบล้างประวัติใหม่บางส่วน (Full refresh rebuild) สัปดาห์ละครั้ง

4. การจัดการข้อมูลตกหล่นหรือมาสายด้วย Lookback Windows ใน SQL

ทฤษฎีและกลไกการทำงาน (How it works): ปัญหาคลาสสิกของท่อส่งแบบส่วนต่างคือ **Late-arriving Data** ข้อมูลเกิดเมื่อวานแต่เพิ่งส่งมาถึงคลังวันนี้เนื่องจากระบบเน็ตเวิร์กของแอปพลิเคชันสะดุด หากเรากรองดึงเฉพาะข้อมูลที่เกิดขึ้นหลังเวลาสูงสุดปัจจุบันของตาราง ข้อมูลที่มาสายกลุ่มนี้จะหลุดรอดการประมวลผลถาวร วิธีแก้ไขคือการเขียนตรรกะเผื่อเวลาคิวรี่ย้อนหลังเรียกว่า **Lookback Window** โดยขยับเวลาเริ่มต้นการดึงข้อมูลย้อนหลังไปเผื่อ เช่น 3 วันล่าสุดเสมอ และพึ่งพา Merge strategy เพื่อลบอัปเดตแถวเก่าซ้ำซ้อน

-- Implementing a 3-day lookback window inside dbt incremental logic
SELECT * FROM {{ ref('stg_events') }}
{% if is_incremental() %}
  -- Scan last 3 days to capture delayed telemetry events
  WHERE event_timestamp >= (SELECT DATEADD(day, -3, MAX(event_timestamp)) FROM {{ this }})
{% endif %}
                    

Use Case ในชีวิตจริง (Real-world Scenario): แอปพลิเคชันมือถือออฟไลน์ของลูกค้าที่จดบันทึกยอดใช้งานเก็บในโทรศัพท์และเพิ่งมาซิงก์ส่งข้อมูลเมื่อลูกค้าหาสัญญาณเน็ตเจอ ระบบ Lookback Window ช่วยให้แต้มสะสมและสถิติยอดใช้ของแอปถูกคำนวณรวมย้อนหลังได้อย่างไม่ผิดพลาด

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การกำหนด Lookback Window กว้างเกินไป (เช่น สแกนย้อนหลัง 30 วันในทุกๆ ชั่วโมง) จะทำให้ท่อประมวลผลใช้เวลานานขึ้นและเสียค่าคอมพิวต์สูงเกินความจำเป็น แก้ไขโดยการประเมินสถิติเวลาล่าช้าเฉลี่ย (SLA tracking) เพื่อกำหนดระยะ Lookback ให้เหมาะสมที่สุด

5. การตรวจสอบความถูกต้องสกีมาและการทำเอกสารแผนผังข้อมูล Lineage

ทฤษฎีและกลไกการทำงาน (How it works): dbt เสริมสร้างความมั่นใจในคุณภาพข้อมูลโดยการนำคุณสมบัติ Schema Testing มาใช้ โดยเราสามารถระบุข้อบังคับความถูกต้องลงในไฟล์ YAML ของโมเดลเพื่อรันคำสั่งเช็คความถูกต้องอัตโนมัติ (เช่น ตรวจสอบความยูนิกของคีย์หลัก, ค่าห้ามเป็น Null, หรือความถูกต้องของความเชื่อมโยงตารางคีย์นอก) นอกจากนี้ dbt ยังสร้างเอกสารและแผนผังเส้นทางความเชื่อมโยงข้อมูล **Data Lineage** ให้ทีมพัฒนาสามารถตรวจสอบการเดินทางของข้อมูลย้อนกลับไปถึงจุดตั้งต้น (Source) ได้ทันที

# Configuration scheme for model assertions and schema testing in YAML
# models/schema.yml
version: 2

models:
  - name: dim_customers
    columns:
      - name: customer_key
        tests:
          - unique
          - not_null
      - name: status
        tests:
          - accepted_values:
              values: ['active', 'inactive', 'pending']
                    

Use Case ในชีวิตจริง (Real-world Scenario): ทุกครั้งที่วิศวกรข้อมูลรวมโค้ดและส่งขึ้นระบบโปรดักชัน ระบบ CI/CD จะสั่งรันคำสั่ง `dbt test` เพื่อตรวจสอบความสมบูรณ์ของยอดสถิติตาราง หากพบว่ามีข้อมูลแถวใดที่คีย์หลักซ้ำซ้อนหรือติดค่า Null ระบบจะสั่งยกเลิกเพื่อไม่ให้ข้อมูลพังไหลขึ้นระบบจริง

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การรันระบบทดสอบที่ซับซ้อนเกินไปบนข้อมูลขนาดใหญ่ระดับร้อยล้านแถวอาจเปลืองค่าประมวลผลคิวรีสูง แก้ไขโดยหลีกเลี่ยงการทดสอบแบบ Join ตารางในชั้นนี้ และย้ายความรับผิดชอบนี้ไปควบคุมที่ขั้นตอน Data Contracts ตั้งแต่ขาเข้าท่อส่งข้อมูล

🛠️ Weekend Sandbox Challenge: Configure Incremental Model in dbt

โจทย์ปฏิบัติการ: จงเขียนระเบียบโมเดล SQL ใน dbt เพื่อบันทึกข้อมูลแบบเพิ่มส่วนต่าง (Incremental Model) โดยระบุคีย์หลักในการทำ Merge และกรองขอบเขตวันเวลารันย้อนหลังเพื่อดึงข้อมูลอย่างคุ้มค่า

-- weekly_sales.sql
{{ config(
    materialized='incremental',
    unique_key='week_id',
    incremental_strategy='merge'
) }}

SELECT 
    DATE_TRUNC('week', order_date) as week_id,
    SUM(amount) as total_revenue
FROM {{ ref('stg_orders') }}
{% if is_incremental() %}
    WHERE order_date >= (SELECT DATEADD(day, -3, MAX(week_id)) FROM {{ this }}) -- 3-day lookback window
{% endif %}
GROUP BY 1
                    

💼 Senior Technical Interview Q&A

Q1: อะไรคือความแตกต่างเชิงกำลังและค่าคิวรี่ระหว่างกลไก Table Materialization และ Incremental Materialization ใน dbt?

A1: Table materialization จะสร้างตารางขึ้นใหม่หมดทุกรอบการรัน (Drop & Recreate) ซึ่งประมวลผลข้อมูลปริมาณยักษ์ซ้ำซาก สิ้นเปลืองทั้งเวลาและค่าใช้จ่ายคลาวด์มหาศาล ในขณะที่ Incremental materialization ดึงเฉพาะแถวข้อมูลใหม่หรือแถวที่เปลี่ยนสเปคมาทับตารางเป้าหมายเดิมผ่านคำสั่ง `MERGE` ช่วยสปีดและประหยัดงบระบบได้สูงสุด

Q2: เราควรดีไซน์แก้ไขปัญหาท่อส่งดึงข้อมูลล่าช้า (Late-arriving data) ในโครงสร้าง Incremental ของ dbt อย่างไร?

A2: กำหนดกรอบการดึงข้อมูลย้อนหลังเผื่อเวลา (Lookback Window) ในคำสั่งกรอง โดยคำนวณย้อนหลัง เช่น `MAX(date) - INTERVAL '3 DAYS'` แทนที่จะดึงเฉพาะจุดล่าสุด และใช้คุณสมบัติ `unique_key` ร่วมกับ `merge` strategy เพื่อให้ dbt ทำลายแถวประวัติเดิมทิ้งก่อนแทรกค่าแก้ไขใหม่

ข้อดี / จุดเด่น & ข้อเสีย / ข้อควรระวัง

ข้อดี / จุดเด่น

  • ทำให้การพัฒนา SQL เป็นระเบียบและลดการเขียนโค้ดซ้ำซ้อนได้ดีเยี่่ยม
  • มีระบบเขียนเช็คคุณภาพข้อมูลและบันทึกเอกสารความสัมพันธ์อัตโนมัติในตัว

ข้อเสีย / ข้อควรระวัง

  • ทำได้แค่ด้านแปลงข้อมูล (T) เท่านั้น ไม่ได้รองรับการดึงข้อมูลจากต้นทางข้ามแหล่ง
  • ต้องอาศัยการวางแผนการประมวลผลรันเครื่องเซิร์ฟเวอร์คลังข้อมูลจริงในการคำนวณ

ปฏิบัติการจริง (Lab Practice)

Bilingual Guide

แล็บ: พัฒนาและรันชุดโมเดลวิเคราะห์ข้อมูลผ่าน dbt

💡 คำแนะนำ & ทริกเด็ด

สำหรับโปรเจกต์ขนาดใหญ่ แนะนำให้เขียนโมเดลทดสอบไว้ในสภาพแวดล้อมจำลอง (Dev schema) เพื่อไม่ให้คิวรีเขียนไปทับตารางจริงบนโปรดักชันจนกว่ารหัสจะผสานสายหลัก