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)
บทที่ 2: การจัดเก็บและสร้างแบบจำลองข้อมูล (Data Storage & Modeling)

การออกแบบโครงสร้างข้อมูล (Star/Snowflake & SCD)

2.3 การออกแบบจำลองตารางข้อมูลและการจัดการประวัติ (Data Modeling & Historical Retention)

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

1. เทคนิค Slowly Changing Dimension (SCD Type 0 ถึง 4)

ทฤษฎีและกลไกการทำงาน (How it works): Slowly Changing Dimensions (SCD) คือสัญญารูปแบบการบันทึกเมื่อค่ามิติธุรกิจมีการเปลี่ยนรูป: - **SCD Type 0**: สภาพคงเดิมถาวร ไม่ยอมรับการแก้ไขใดๆ เช่น วันเกิดลูกค้า - **SCD Type 1**: เขียนแก้ไขทับฟิลด์เดิมทันทีโดยไม่บันทึกประวัติเก่า (ไม่มีประวัติย้อนหลัง) - **SCD Type 2**: วิธีการมาตรฐานระดับองค์กร โดยสร้างแถวใหม่ขึ้นมาบันทึกประวัติการแก้ไข และคุมช่วงเวลาใช้งานจริงด้วยคอลัมน์ `valid_from`, `valid_to` และแฟล็กบอกความใหม่ `is_current` - **SCD Type 3**: เพิ่มคอลัมน์ใหม่คู่ขนาน (เช่น `current_city` และ `previous_city`) บันทึกการย้อนอัปเดตย้อนหลังได้เพียง 1 ขั้น - **SCD Type 4**: สร้างตารางประวัติประวัติต่างหากแยกจากตารางมิติหลัก (History Table) เพื่อป้องกันขนาดตารางหลักบวมโต

-- SQL simulation of SCD Type 2 historic row creation
-- 1. Expire current address record row
UPDATE dim_customers 
SET valid_to = CURRENT_DATE, is_current = FALSE 
WHERE customer_key = 101 AND is_current = TRUE;

-- 2. Insert new record row with new address values
INSERT INTO dim_customers (customer_key, name, city, valid_from, valid_to, is_current)
VALUES (101, 'Chawin', 'Bangkok', CURRENT_DATE, '9999-12-31', TRUE);
                    

Use Case ในชีวิตจริง (Real-world Scenario): เมื่อลูกค้าเปลี่ยนที่อยู่อาศัยในการจัดส่งสินค้า การใช้ SCD Type 2 ทำให้เมื่อวิเคราะห์ข้อมูลการขายย้อนหลังของเดือนก่อน ยอดซื้อขายจะถูกจัดประเภทอยู่ในจังหวัดเก่าซึ่งเป็นที่อยู่จริงขณะเกิดรายการได้อย่างถูกต้อง

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

2. การออกแบบสกีมาแบบ Kimball: Star Schema และ Snowflake Schema

ทฤษฎีและกลไกการทำงาน (How it works): สถาปัตยกรรมคลังข้อมูลเชิงมิติ (Dimensional Modeling) นำเสนอโครงสร้างการจัดกลุ่มข้อมูลออกเป็น 2 บทบาท: - **Fact Tables**: ตารางเก็บค่าตัวเลขที่เกิดขึ้นจากการทำธุรกรรม (เช่น ยอดเงิน, จำนวนชิ้น) มีคีย์หลักเชื่อมไปยังตารางแวดล้อม - **Dimension Tables**: ตารางเก็บคำอธิบายบริบทแวดล้อม (เช่น รายละเอียดลูกค้า, ข้อมูลสินค้า) รูปแบบการจัดแบ่งมี 2 แบบ: - **Star Schema**: ตารางมิติถูกจัดรูปแบบขยาย (Denormalized) ให้แบนราบในตารางเดียว ไม่มีการเชื่อม `JOIN` ซ้อน ช่วยให้อ่านคิวรี่เร็วสุด - **Snowflake Schema**: ตารางมิติถูกแตกรองรับมาตรฐานสกีมาแบบบรรทัดฐาน (Normalized) เพื่อประหยัดพื้นที่จัดเก็บข้อมูล แต่แลกมาด้วยความเร็วคิวรี่ที่ช้าลงจากจ๊อยน์ที่ซับซ้อน

-- Star Schema Query Layout (Simplistic and optimized for analytical scanning)
SELECT d.city, SUM(f.sales_amount) as total_sales
FROM fact_sales f
JOIN dim_customers d ON f.customer_key = d.customer_key
GROUP BY d.city;
                    

Use Case ในชีวิตจริง (Real-world Scenario): คลังข้อมูลธุรกิจค้าปลีกออกแบบด้วย Star Schema เพื่อสนับสนุนให้โปรแกรมแดชบอร์ด BI สามารถดึงสรุปประมวลผลยอดขายแยกตามเขตและประเภทสินค้าล้านระเบียนมาแสดงผลได้แบบตอบสนองรวดเร็วทันใจผู้บริหาร

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

3. สถาปัตยกรรมข้อมูลขั้นสูง Data Vault 2.0 (Hubs, Links, Satellites)

ทฤษฎีและกลไกการทำงาน (How it works): **Data Vault 2.0** ออกแบบมาเพื่อคลังข้อมูลระดับยักษ์ (Enterprise Scale) ที่ต้องป้อนข้อมูลเข้าขนานกันจากหลากหลายระบบ โดยมีโครงสร้างแยกออบเจกต์เป็น 3 ประเภทหลัก: - **Hubs**: เก็บเฉพาะคีย์รหัสธุรกิจหลัก (Business Keys) และคีย์แฮชระบุตัวตน ปราศจากรายละเอียดบริบท - **Links**: เก็บความสัมพันธ์และการทำจ๊อยน์เชื่อมโยงระหว่าง Hubs ด้วยค่าแฮช - **Satellites**: เก็บรายละเอียดและประวัติความเปลี่ยนแปลงของ Hub หรือ Link นั้นๆ พร้อมวันเวลาบันทึก สถาปัตยกรรมนี้รองรับการเขียนข้อมูลขนานกันได้เต็มสเกลเพราะไม่มีการล็อกตารางหรือเช็คเงื่อนไขความสัมพันธ์ต่างตาราง (Foreign Keys) ระหว่างนำเข้าข้อมูล

-- Data Vault Hub and Satellite schema design templates
CREATE TABLE hub_customers (
    customer_hash_key CHAR(32) PRIMARY KEY, -- MD5/SHA-256 of customer_id
    customer_id VARCHAR(50) NOT NULL,
    load_timestamp TIMESTAMP NOT NULL,
    record_source VARCHAR(50) NOT NULL
);

CREATE TABLE sat_customer_details (
    customer_hash_key CHAR(32) REFERENCES hub_customers(customer_hash_key),
    hash_diff CHAR(32) NOT NULL, -- Used to quickly detect if attributes changed
    email VARCHAR(100),
    phone VARCHAR(20),
    load_timestamp TIMESTAMP NOT NULL,
    record_source VARCHAR(50) NOT NULL,
    PRIMARY KEY (customer_hash_key, load_timestamp)
);
                    

Use Case ในชีวิตจริง (Real-world Scenario): บริษัทประกันภัยขนาดใหญ่ที่มีระบบไอทีและฐานข้อมูลลูกค้าแยกย่อย 10 ระบบ ดึงข้อมูลเข้ามารวมกันใน Data Vault โดยเชื่อมโยงผ่านเลขบัตรประชาชนเป็น Hub และให้รายละเอียดที่ต่างกันของแต่ละฐานข้อมูลแยกเก็บเป็น Satellites อิสระ

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การคิวรี่หาข้อมูลปัจจุบันจาก Data Vault มีความยุ่งยากสูงมากเพราะต้องรันคำสั่ง Join ข้อมูลข้าม Hub และ Satellite หลายสิบตาราง แก้ไขโดยสร้างตารางสรุปผลกลาง **Information Mart** ที่แปลงรูปแบบ Data Vault ให้กลายเป็นตารางมิติ Star Schema สำหรับเสิร์ฟผู้ใช้งานปลายทาง

4. การออกแบบสกีมาแบบหนึ่งต่อกลุ่ม (One-to-Many) และกลุ่มต่อกลุ่ม (Many-to-Many)

ทฤษฎีและกลไกการทำงาน (How it works): การวางความสัมพันธ์ฐานข้อมูลเชิงสัมพันธ์ต้องควบคุมตามหลักการออกแบบ: - **One-to-Many (1:N)**: วางคีย์หลักของตารางแม่ (เช่น `customer_id`) ไปเป็นคีย์นอก (Foreign Key) ในตารางลูก (เช่น `orders`) คอนฟิกเพื่อคุมความถูกต้องข้อมูลเชิงโครงสร้าง - **Many-to-Many (N:M)**: ต้องแก้ไขโดยสร้างตารางทางผ่านตรงกลางเรียกว่า **Junction Table** (หรือ Bridge Table) เพื่อเก็บคู่ความสัมพันธ์ของคีย์จากสองตารางหลัก เพื่อป้องกันตารางข้อมูลบวมและขัดต่อหลักการ Normalization

-- Junction Table implementing Many-to-Many relationship with composite primary keys
CREATE TABLE orders_products (
    order_id UUID REFERENCES orders(order_id),
    product_id UUID REFERENCES products(product_id),
    quantity INTEGER NOT NULL CHECK (quantity > 0),
    PRIMARY KEY (order_id, product_id)
);
-- Compose index on the reverse lookup column to speed up queries
CREATE INDEX idx_products_orders ON orders_products(product_id);
                    

Use Case ในชีวิตจริง (Real-world Scenario): ในฐานข้อมูลการศึกษา คลาสเรียนหนึ่งคลาสมีนักเรียนลงเรียนได้หลายคน และนักเรียนหนึ่งคนก็สามารถลงเรียนได้หลายคลาสเรียน ระบบจึงใช้ตารางทางผ่าน `student_classes` เก็บประวัติความสัมพันธ์เพื่อให้เขียนคิวรีคอร์สเรียนได้ลื่นไหล

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การลืมสร้างดัชนีคุมพิกัดย้อนกลับบน Junction Table ส่งผลให้การ Join ข้อมูลย้อนทางคิวรีทำงานช้าเป็นเต่าคลาน แก้ไขโดยสร้างดัชนีเฉพาะเสริม (Composite Index หรือ Single Index) บนคอลัมน์คีย์นอกตัวที่สองในตารางทางผ่านเสมอ

5. การทำ Denormalization และ Materialized Views สำหรับเอนจิ้น OLAP

ทฤษฎีและกลไกการทำงาน (How it works): ในขณะที่ระบบหลักต้องการการทำ Normalization (แยกตารางเพื่อกันข้อมูลซ้ำ) ระบบประมวลผลเชิงวิเคราะห์ (OLAP) จะกลับขั้วโดยทำ **Denormalization** (การหลอมรวมตารางเข้าด้วยกันล่วงหน้า) เพื่อเลี่ยงคำสั่ง Join สถาปัตยกรรมระดับเอนจิ้นจะใช้ **Materialized Views** ซึ่งจะประมวลผลคิวรีล่วงหน้าและเขียนสรุปบันทึกสเตทลงบนแผ่นดิสก์ถาวรเสมือนตารางจริง ทำให้เวลาเรียกอ่านไม่ต้องประมวลผลใหม่ตั้งแต่ต้นสาย

-- Creating Materialized View on analytical query in ClickHouse
CREATE MATERIALIZED VIEW mv_daily_sales
ENGINE = SummingMergeTree()
PRIMARY KEY (sale_date, product_id)
AS SELECT 
    toDate(created_at) as sale_date,
    product_id,
    sum(amount) as daily_revenue
FROM db.sales
GROUP BY sale_date, product_id;
                    

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

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ในระบบ relational ดั้งเดิม ข้อมูลใน Materialized View จะไม่ยอมรับการอัปเดตแบบเรียลไทม์จนกว่าจะรันคำสั่ง `REFRESH MATERIALIZED VIEW` ซึ่งกินพลังคอมพิวต์สูง แก้ไขโดยใช้เอนจิ้น OLAP อย่าง ClickHouse ที่มีระบบ `SummingMergeTree` คอยอัปเดตพาร์ทิชันสรุปผลอัตโนมัติเมื่อมีข้อมูลใหม่ไหลเข้าท่อ

🛠️ Weekend Sandbox Challenge: Star Schema DDL Design

โจทย์ปฏิบัติการ: จงเขียนรหัส SQL สร้างตารางสัญนิยมแบบจำลองมิติ (Dimensional Modeling) โดยจัดเก็บยอดขายสินค้า ประกอบด้วยตารางยอดทำรายการหลัก (Fact Table) และตารางคุณลักษณะประเภทสินค้าและลูกค้า (Dimension Tables)

-- Create Dim Customers
CREATE TABLE dim_customers (
    customer_key INT PRIMARY KEY,
    customer_id VARCHAR(50) UNIQUE,
    customer_segment VARCHAR(20)
);

-- Create Fact Sales
CREATE TABLE fct_sales (
    sales_key INT PRIMARY KEY,
    date_key INT,
    customer_key INT REFERENCES dim_customers(customer_key),
    amount DECIMAL(12,2),
    quantity INT
);
                    

💼 Senior Technical Interview Q&A

Q1: การออกแบบโมเดลสามมิติแบบ Star Schema (แนวคิด Kimball) มีข้อได้เปรียบเชิงคำนวณการวิเคราะห์อย่างไรเมื่อเทียบกับการออกแบบแบบนอร์มัลไลเซชัน (Inmon)?

A1: Star Schema ออกแบบมาเพื่อเพิ่มความเร็วในการสืบค้นวิเคราะห์เชิงลึก (OLAP) โดยลดจำนวนคำสั่ง JOIN เพื่อให้อ่านประมวลผลข้อมูลได้รวดเร็วขึ้น แม้จะเปลืองดิสก์จากการเก็บข้อมูลซ้ำซ้อนบ้าง (Denormalization) ในขณะที่ Inmon เน้นสถาปัตยกรรมแบบ 3NF เพื่อลดความซ้ำซ้อนประหยัดที่เขียนของ OLTP แต่ต้องใช้จำนวน JOIN มหาศาลวิเคราะห์ช้า

Q2: จงอธิบายวิธีการออกแบบ Slowly Changing Dimension (SCD) Type 2 ในระบบคลังข้อมูล และวิธีคิวรีเพื่อดึงข้อมูลประวัติในอดีต?

A2: SCD Type 2 จัดเก็บประวัติข้อมูลดิบย้อนหลังโดยการสร้างแถวข้อมูลใหม่เมื่อมีการแก้ไขคุณลักษณะ พร้อมเพิ่มคอลัมน์ควบคุม `valid_from`, `valid_to` และ `is_current`. เมื่อต้องการดึงประวัติในอดีต ให้เขียนเงื่อนไขกรอง: `WHERE target_date BETWEEN valid_from AND valid_to` เพื่อให้ได้ค่ามิติ ณ เวลานั้นอย่างถูกต้อง

🕸️ Interactive Star Schema Designer

Interactive Schema

เป้าหมาย: สร้างความสัมพันธ์แบบ Star Schema โดยจับคู่ Foreign Key จากตารางความจริง (Fact Table) ไปยัง Primary Key ของตารางมิติ (Dimension Tables)

dim_customers Dimension
🔑 customer_key PK (Int)
customer_name
country
fact_sales Fact Table
sale_key PK (Int)
customer_key
product_key
amount Metric
dim_products Dimension
🔑 product_key PK (Int)
product_name
category

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

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

  • ช่วยประหยัดเวลาการประมวลผลรายงานวิเคราะห์เพราะการ JOIN ตารางเรียบง่าย
  • รายงานวิเคราะห์อดีตมีความถูกต้อง 100% ไม่สูญเสียประวัติการย้ายหมวดหมู่

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

  • ต้องเขียนโค้ด ETL ที่ซับซ้อนขึ้นในการจัดการคอลัมน์สถานะและช่วงเวลาใช้งาน
  • ตารางบวมและกินพื้นที่ดิสก์เยอะขึ้นเพราะมีการแทรกแถวใหม่ทุกครั้งที่มีการอัปเดต

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

Bilingual Guide

แล็บ: สร้างแบบจำลองตารางประวัติข้อมูลแบบ SCD Type 2

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

หลีกเลี่ยงการใช้ SCD Type 2 กับตารางมิติที่มีขนาดขยายตัวเร็วมาก (High-Cardinality) เพราะจำนวนแถวจะบวมขึ้นเรื่อยๆ แนะนำให้แยกฟิลด์ที่เปลี่ยนบ่อยออกไปเป็นตารางย่อยแยกต่างหาก