บทนำ
DuckDB มีจุดแข็งด้านการ อ่านและเขียนไฟล์ข้อมูลหลายรูปแบบโดยตรงจาก SQL โดยไม่ต้องพึ่งเครื่องมือภายนอก ไม่ว่าจะเป็น CSV, Excel, หรือ Parquet ความสามารถนี้ทำให้ DuckDB เหมาะอย่างยิ่งสำหรับงานวิเคราะห์ข้อมูลที่ต้องเชื่อมต่อกับไฟล์จากหลายแหล่งและทำงานได้อย่างคล่องตัวภายในสภาพแวดล้อมเดียว
บทนี้จะแนะนำวิธีทำงานกับรูปแบบไฟล์ที่พบได้บ่อยในงานจริง พร้อมยกตัวอย่างจากฐานข้อมูล PiCha Tea House เพื่อให้ผู้เรียนสามารถนำ DuckDB ไปใช้ร่วมกับเครื่องมือและชุดข้อมูลที่มีอยู่ได้อย่างสะดวกและมีประสิทธิภาพ
ทำงานกับไฟล์ CSV
CSV (Comma-Separated Values) เป็นไฟล์ข้อความที่เก็บข้อมูลในรูปแบบตาราง แต่ละแถวคือหนึ่งระเบียน และแต่ละคอลัมน์ คั่นด้วยเครื่องหมายคอมมา (หรืออักขระตัวคั่นอื่นเช่น ; หรือ |) ระบบส่วนใหญ่รองรับ CSV จึงเป็นมาตรฐานการแลกเปลี่ยนข้อมูลที่นิยมที่สุดในงานจริง
DuckDB อ่านไฟล์ CSV ได้โดยตรงผ่านชื่อไฟล์หรือฟังก์ชัน read_csv() ซึ่งสามารถตรวจจับ schema อัตโนมัติ และรองรับ option สำหรับกรณีไฟล์มีรูปแบบพิเศษ เช่น ตัวคั่นที่ไม่ใช่คอมมา encoding ที่ต่างออกไป หรือค่าที่ใช้แทน NULL
การอ่าน CSV
DuckDB รองรับการอ่านไฟล์ CSV โดยตรงด้วย SELECT โดยไม่ต้องใช้ฟังก์ชัน read_csv() ให้ยุ่งยาก
วิธีที่ 1 อ่านตรงจากชื่อไฟล์ (แบบสั้นที่สุด)
อ่านแบบระบุตำแหน่งที่ตั้งของไฟล์ (File Path)
เส้นทางไฟล์ './data/stores.csv' ใช้สัญลักษณ์ . แทน โฟลเดอร์ปัจจุบัน (Current Working Directory) ซึ่งคือโฟลเดอร์ที่เปิด DuckDB หรือรันสคริปต์อยู่
| รูปแบบ Path | ตัวอย่าง | ความหมาย |
|---|---|---|
| Current folder | 'stores.csv' |
ไฟล์อยู่ในโฟลเดอร์ตอนรัน DuckDB |
| Relative | './data/stores.csv' |
นับจากโฟลเดอร์ที่รันสคริปต์ |
| Absolute (Linux, macOS) | '/home/user/project/data/stores.csv' |
ระบุตำแหน่งเต็มจาก root |
| Absolute (Windows) | 'c:\temp\stores.csv' หรือ 'c:/temp/stores.csv' |
ระบุตำแหน่งเต็มจาก root ในไดรฟ์ C (ใชได้ทั้ง / และ \ ในการแบ่ง file path) |
ข้อดีของ Relative Path คือย้ายโฟลเดอร์โปรเจกต์ไปที่ไหนก็ยังใช้งานได้ ต่างจาก Absolute Path ที่ต้องแก้ไขทุกครั้งที่เปลี่ยนเครื่องหรือเปลี่ยน User
วิธีที่ 2 อ่านจาก URL โดยตรง
วิธีที่ 3 ใช้ฟังก์ชัน read_csv() (ควบคุมได้มากกว่า)
วิธีที่ 4 สร้างตารางจาก CSV
วิธีที่ 5 ใช้ COPY เมื่อมี schema ตายตัวล่วงหน้า
-- สร้างตาราง stores และนำเข้าข้อมูลจากไฟล์ CSV
CREATE TABLE stores (
store_id INTEGER, -- รหัสสาขา
store_name VARCHAR, -- ชื่อสาขา
branch_type VARCHAR, -- ประเภทสาขา (เช่น mall, office)
district VARCHAR -- ย่านที่ตั้งสาขา
);
COPY stores FROM 'stores.csv' (HEADER true); -- นำเข้าข้อมูลจาก CSV โดยใช้แถวแรกเป็น headerParameters ที่ใช้บ่อย
| Parameter | ความหมาย | ค่าเริ่มต้น | ตัวอย่าง |
|---|---|---|---|
header |
แถวแรกคือชื่อคอลัมน์ | false |
header = true |
delim |
ตัวคั่น field | , |
delim = '|' |
encoding |
encoding ของไฟล์ | utf-8 |
encoding = 'latin-1' |
skip |
ข้ามกี่แถวแรก | 0 |
skip = 2 |
null |
ค่าที่แทน NULL | '' |
null = 'N/A' |
compression |
การบีบอัด | auto |
compression = 'gzip' |
all_varchar |
อ่านทุกคอลัมน์เป็น text | false |
all_varchar = true |
ตัวอย่างใช้หลาย parameter พร้อมกัน:
-- อ่านไฟล์ CSV ที่บีบอัดด้วย gzip และใช้ | เป็นตัวคั่น
SELECT * FROM read_csv(
'transactions_export.csv', -- ชื่อไฟล์ CSV ที่ต้องการอ่าน
header = true, -- แถวแรกเป็น header (ชื่อ column)
delim = '|', -- ใช้ | เป็นตัวคั่น column
encoding = 'utf-8', -- อ่านไฟล์ด้วย encoding UTF-8
null = 'NULL', -- แปลงค่า 'NULL' ในไฟล์เป็น NULL
compression = 'gzip' -- ไฟล์ถูกบีบอัดด้วย gzip
);การเขียน CSV
คำสั่ง COPY ... TO ส่งออกข้อมูลจาก DuckDB ไปยังไฟล์ CSV โดยมีรูปแบบการใช้งาน 2 แบบ
แบบที่ 1 ส่งออกทั้งตาราง ระบุชื่อตารางโดยตรง เหมาะเมื่อต้องการข้อมูลทุกแถวทุกคอลัมน์ เช่น ต้องการส่งออกตาราง transactions ไปเป็นไฟล์ transactions_export.csv
แบบที่ 2 ส่งออกจาก Query ใส่คำสั่ง SELECT ไว้ในวงเล็บ ทำให้กรองข้อมูล เลือกคอลัมน์ หรือ JOIN ตารางก่อนส่งออกได้ในขั้นตอนเดียว
ตัวเลือกที่ใช้บ่อยในวงเล็บท้ายคำสั่ง
| ตัวเลือก | ค่าตัวอย่าง | ความหมาย |
|---|---|---|
HEADER |
true / false |
ให้บรรทัดแรกแสดงเป็นชื่อคอลัมน์หรือไม่ |
DELIMITER |
',' / '\t' |
ตัวคั่นข้อมูล (ค่าเริ่มต้นคือจุลภาค) |
QUOTE |
'"' |
อักขระครอบค่าที่มีตัวคั่นอยู่ภายใน |
NULL |
'' |
แทนค่า NULL ด้วยข้อความที่กำหนด |
ทำงานกับไฟล์ Excel
สำหรับผู้ที่ใช้ Excel เป็นหลัก DuckDB สามารถอ่านและเขียนไฟล์ .xlsx ได้ผ่าน excel extension การใช้งานพื้นฐานเหมือนกับ CSV แต่เพิ่มความสามารถเรื่อง sheet และช่วง cell รวมถึงส่งออกรายงานในรูปแบบที่ผู้ใช้ทั่วไปเปิดอ่านได้ทันที
DuckDB ต้องติดตั้ง extension นี้ครั้งแรกด้วย INSTALL excel; และโหลดทุกครั้งเมื่อเริ่ม session ใหม่ด้วย LOAD excel;
ติดตั้ง Excel Extension
ก่อนการ import/export ข้อมูล ต้องทำการติดตั้งส่วนขยาย extension พร้อมโหลด extension เข้า session ปัจจุบัน (ทำทุกครั้งที่เปิด session ใหม่)
ทำโดยรันโปรแกรม duckdb จากนั้นพิมพ์คำสั่งดังนี้
การอ่าน Excel
วิธีที่ 1 อ่านจากชื่อไฟล์
วิธีที่ 2 อ่านจาก URL โดยตรง
วิธีที่ 3 ใช้ฟังก์ชัน read_xlsx() พร้อมกำหนดค่าให้พารามิเตอร์
วิธีที่ 4 ระบุ range ที่ต้องการ เช่น ต้องการอ่านข้อมูลเฉพาะที่อยู่ใน A1:E100 จากชีท Revenue ในไฟล์ report.xlsx
Parameters ของ read_xlsx()
| Parameter | ความหมาย | ค่าเริ่มต้น |
|---|---|---|
header |
แถวแรกคือชื่อคอลัมน์ | false |
sheet |
ชื่อ sheet ที่ต้องการอ่าน | sheet แรก |
range |
cell range เช่น A1:E50 |
ทั้งหมด |
all_varchar |
อ่านทุก cell เป็น text | false |
ignore_errors |
แทน cell ที่อ่านไม่ได้ด้วย NULL | false |
stop_at_empty |
หยุดอ่านเมื่อเจอแถวว่าง | false |
การส่งออกเป็นไฟล์ Excel
มีรูปแบบการทำงานเหมือนกับการทำงานกับ CSV ต่างกันเพียงพารามิเตอร์ที่ใช้ในการระบุรายละเอียดบางอย่าง
-- ส่งออกตารางเป็น Excel
COPY stores TO 'stores_export.xlsx' (FORMAT xlsx, HEADER true);
-- ส่งออก query result พร้อมระบุชื่อ sheet
COPY (
SELECT
s.store_name, -- ชื่อสาขา
s.branch_type, -- ประเภทสาขา
COUNT(*) AS order_count, -- จำนวนออร์เดอร์ทั้งหมด
ROUND(SUM(t.net_sales_thb), 0) AS total_revenue -- รายได้รวม (บาท)
FROM transactions t
JOIN stores s ON t.store_id = s.store_id -- เชื่อมตารางสาขาเพื่อดึงชื่อสาขา
GROUP BY s.store_name, s.branch_type -- จัดกลุ่มตามสาขาและประเภท
ORDER BY total_revenue DESC -- เรียงจากรายได้สูงสุดไปต่ำสุด
) TO 'revenue_report.xlsx' (
FORMAT xlsx, -- บันทึกในรูปแบบ Excel
HEADER true, -- เพิ่มแถว header ในไฟล์
SHEET 'Revenue by Store' -- ชื่อ sheet ในไฟล์ Excel
);ทำงานกับไฟล์ Parquet
Parquet เป็น binary format ที่ออกแบบมาสำหรับงาน analytics โดยเฉพาะ ต่างจาก CSV ที่เก็บข้อมูลแบบ row-by-row เพราะ Parquet เก็บแบบ columnar ทำให้เวลา query ดึงเฉพาะบางคอลัมน์ ระบบอ่านเฉพาะส่วนที่จำเป็น ส่งผลให้เร็วกว่า CSV อย่างชัดเจนในชุดข้อมูลใหญ่ นอกจากนี้ Parquet ยังบีบอัดข้อมูลได้ดีกว่า CSV เพราะข้อมูลในแต่ละ column มักมีรูปแบบซ้ำกัน ทำให้ compression algorithm ทำงานได้อย่างมีประสิทธิภาพ
DuckDB อ่านและเขียนไฟล์ Parquet ได้โดยตรงผ่านฟังก์ชัน read_parquet() และคำสั่ง COPY ... TO
การอ่าน Parquet
วิธีที่ 1 อ่านตรงจากชื่อไฟล์
วิธีที่ 2 อ่านจาก URL โดยตรง
วิธีที่ 3 ใช้ฟังก์ชัน read_parquet()
วิธีที่ 4 อ่านหลายไฟล์พร้อมกันด้วย glob pattern
การส่งออกเป็นไฟล์ Parquet
-- ส่งออกตาราง transactions ทั้งหมดเป็นไฟล์ Parquet
COPY transactions TO 'transactions.parquet' (FORMAT parquet);
-- ส่งออกพร้อมระบุ compression algorithm
COPY transactions TO 'transactions.parquet' (
FORMAT parquet, -- บันทึกในรูปแบบ Parquet
COMPRESSION zstd -- บีบอัดด้วย zstd (ขนาดเล็ก ความเร็วสมดุล)
);
-- ส่งออกเฉพาะ column ที่จำเป็น เพื่อลดขนาดไฟล์
COPY (
SELECT transaction_id, order_date, net_sales_thb, store_id -- เลือกเฉพาะ 4 column
FROM transactions
) TO 'transactions_slim.parquet' (
FORMAT parquet, -- บันทึกในรูปแบบ Parquet
COMPRESSION snappy -- บีบอัดด้วย snappy (เร็วกว่า zstd แต่ขนาดใหญ่กว่าเล็กน้อย)
);Compression Options
| Algorithm | ความเร็ว | ขนาดไฟล์ | เหมาะกับ |
|---|---|---|---|
snappy (default) |
เร็ว | ปานกลาง | งานทั่วไป |
zstd |
ปานกลาง | เล็กมาก | เก็บ archive ระยะยาว |
lz4 |
เร็วมาก | ปานกลาง | อ่าน/เขียนบ่อย |
uncompressed |
เร็วสุด | ใหญ่สุด | debug / ทดสอบ |
ดู Metadata ของไฟล์ Parquet
อ่านหลายไฟล์พร้อมกัน (ทุกฟอร์แมต)
ในงานจริงเรามักเก็บข้อมูลแยกเป็นหลายไฟล์ เช่น รายเดือนหรือรายวัน DuckDB รองรับ glob pattern เพื่ออ่านหลายไฟล์เป็น dataset เดียวได้ในหลายรูปแบบไฟล์ เช่น CSV และ Parquet
-- อ่าน CSV ทุกไฟล์ในโฟลเดอร์
SELECT * FROM 'monthly_data/*.csv';
-- อ่านหลายโฟลเดอร์สำหรับ CSV
SELECT * FROM read_csv(['data/2026-02/*.csv', 'data/2026-03/*.csv']);
-- รู้ว่าข้อมูลมาจากไฟล์ไหน
SELECT filename, * FROM 'monthly_data/*.csv';
-- รวม Parquet หลายไฟล์ที่ schema ต่างกันด้วย union_by_name
SELECT * FROM read_parquet('data/*.parquet', union_by_name = true);EXPORT/IMPORT DATABASE สำรองทั้งฐานข้อมูล
จากที่เรา export/import ระดับตารางหรือไฟล์เดี่ยวกันมาแล้ว DuckDB ยังมีคำสั่งสำหรับ backup หรือย้ายฐานข้อมูล ทั้งฐาน ผ่าน EXPORT DATABASE และ IMPORT DATABASE
Export ทั้งฐานข้อมูล
ไฟล์ที่ได้ในโฟลเดอร์ backup/picha_backup/
backup/picha_backup/
├── schema.sql ← CREATE TABLE, CREATE VIEW ทั้งหมด
├── load.sql ← COPY statements สำหรับโหลดข้อมูลกลับ
├── transactions.csv ← ข้อมูลแต่ละตาราง
├── stores.csv
├── customers.csv
└── ...
Import ทั้งฐานข้อมูล
คำสั่งนี้จะ 1. อ่าน schema.sql → สร้างตารางทั้งหมดขึ้นใหม่ 2. อ่าน load.sql → โหลดข้อมูลกลับเข้าทุกตาราง
เปรียบเทียบรูปแบบไฟล์
| รูปแบบ | อ่าน | เขียน | ขนาดไฟล์ | ความเร็ว | เหมาะกับ |
|---|---|---|---|---|---|
| CSV | ✅ | ✅ | ใหญ่ | ปานกลาง | แลกเปลี่ยนข้อมูล, Excel |
| Excel (.xlsx) | ✅ | ✅ | ปานกลาง | ปานกลาง | รายงาน, ผู้ใช้ทั่วไป |
| Parquet | ✅ | ✅ | เล็กมาก | เร็วมาก | Analytics, Big Data |
แนะนำ: ใช้ CSV สำหรับแลกเปลี่ยนข้อมูลทั่วไป, Parquet สำหรับเก็บข้อมูลขนาดใหญ่, Excel สำหรับส่งรายงานให้ทีมที่ไม่ได้ใช้ SQL
สรุป
บทนี้ครอบคลุมความสามารถด้านการนำเข้าและส่งออกข้อมูลของ DuckDB ซึ่งเป็นจุดเด่นสำคัญที่ช่วยให้ระบบนี้แตกต่างจากฐานข้อมูลทั่วไป โดยเฉพาะในงานวิเคราะห์ข้อมูลที่ต้องเชื่อมต่อกับไฟล์หลายรูปแบบและทำงานได้รวดเร็วภายในสภาพแวดล้อมเดียว
ในงานจริง CSV เป็นรูปแบบไฟล์ที่พบได้บ่อยที่สุด และ DuckDB สามารถอ่านได้ผ่าน read_csv() พร้อมความสามารถในการตรวจจับ schema โดยอัตโนมัติ นอกจากนี้ยังรองรับการกำหนด option ได้หลากหลาย เช่น ตัวคั่นข้อมูล encoding และค่าที่ใช้แทน NULL ทำให้เหมาะกับการนำเข้าข้อมูลจากแหล่งที่มีรูปแบบแตกต่างกัน
สำหรับไฟล์ Excel ผู้ใช้ต้องติดตั้ง excel extension ก่อนจึงจะสามารถอ่านและเขียนไฟล์ .xlsx ได้โดยตรง เมื่อเตรียมส่วนนี้เรียบร้อยแล้ว DuckDB ก็สามารถใช้เป็นเครื่องมือเชื่อมระหว่างงานวิเคราะห์ข้อมูลกับการส่งรายงานให้ทีมที่ทำงานบน Excel ได้อย่างสะดวก
ส่วน Parquet เป็นรูปแบบไฟล์แบบไบนารีที่ออกแบบมาสำหรับงาน analytics โดยเฉพาะ เนื่องจากจัดเก็บข้อมูลแบบ columnar จึงช่วยให้การอ่านข้อมูลทำได้รวดเร็วและบีบอัดข้อมูลได้มีประสิทธิภาพกว่ารูปแบบ CSV อย่างชัดเจน รูปแบบนี้จึงเหมาะกับชุดข้อมูลขนาดใหญ่และการแลกเปลี่ยนข้อมูลระหว่างทีม โดยรองรับการบีบอัดหลายแบบ เช่น zstd และ snappy
โดยสรุป คำสั่งหลักที่ใช้มีอยู่ 2 กลุ่ม ได้แก่ read_*() สำหรับอ่านไฟล์เข้ามาใช้ใน query และ COPY ... TO สำหรับส่งออกผลลัพธ์เป็นไฟล์ ทั้งสองแนวทางทำงานได้โดยตรงภายใน SQL โดยไม่จำเป็นต้องพึ่งเครื่องมือภายนอก จึงทำให้ DuckDB เป็นทางเลือกที่มีความคล่องตัวสูงสำหรับงาน data pipeline และการวิเคราะห์ข้อมูลในทางปฏิบัติ
คำถามท้ายบท
- ส่งออกรายชื่อสาขาทั้งหมด (store_name, branch_type, district) เรียงตามชื่อสาขา ไปเป็นไฟล์ CSV ชื่อ
stores_export.csvโดยใช้|เป็นตัวคั่น
คลิกเพื่อดูเฉลย
-- ส่งออกข้อมูลสาขาเป็นไฟล์ CSV โดยใช้ | เป็นตัวคั่น
COPY (
SELECT
store_name, -- ชื่อสาขา
branch_type, -- ประเภทสาขา
district -- ย่านที่ตั้ง
FROM stores
ORDER BY store_name -- เรียงตามชื่อสาขา
) TO 'stores_export.csv' (
HEADER true, -- เพิ่มแถว header ในไฟล์
DELIMITER '|' -- ใช้ | เป็นตัวคั่น column
);- ส่งออกสรุปรายได้รายสาขา (store_name, order_count, total_revenue) ไปเป็นไฟล์ Excel ชื่อ
revenue_by_store.xlsxโดยตั้งชื่อ sheet ว่าSummary
คลิกเพื่อดูเฉลย
-- ส่งออกสรุปรายได้และจำนวนออร์เดอร์แยกตามสาขาเป็นไฟล์ Excel
INSTALL excel;
LOAD excel;
COPY (
SELECT
s.store_name, -- ชื่อสาขา
COUNT(*) AS order_count, -- จำนวนออร์เดอร์ทั้งหมด
ROUND(SUM(t.net_sales_thb), 0) AS total_revenue -- รายได้รวม (บาท)
FROM transactions t
JOIN stores s ON t.store_id = s.store_id -- เชื่อมตารางสาขาเพื่อดึงชื่อสาขา
GROUP BY s.store_name -- จัดกลุ่มตามสาขา
ORDER BY total_revenue DESC -- เรียงจากรายได้สูงสุดไปต่ำสุด
) TO 'revenue_by_store.xlsx' (
FORMAT xlsx, -- บันทึกในรูปแบบ Excel
HEADER true, -- เพิ่มแถว header ในไฟล์
SHEET 'Summary' -- ชื่อ sheet ในไฟล์ Excel
);- ส่งออกตาราง
transactionsเป็นไฟล์ Parquet ชื่อtransactions.parquetโดยใช้ compression แบบzstdเพื่อประหยัดพื้นที่
- อ่านไฟล์ CSV ชื่อ
new_customers.csvที่ใช้;เป็นตัวคั่น มี header และค่าN/Aแทน NULL แล้ว SELECT 5 แถวแรก
คลิกเพื่อดูเฉลย
-- อ่านไฟล์ CSV ตัวอย่าง 5 แถวแรก เพื่อตรวจสอบโครงสร้างและข้อมูลก่อน import
SELECT *
FROM read_csv(
'new_customers.csv', -- ชื่อไฟล์ CSV ที่ต้องการอ่าน
header = true, -- แถวแรกเป็น header (ชื่อ column)
delim = ';', -- ใช้ ; เป็นตัวคั่น column
null = 'N/A' -- แปลงค่า 'N/A' ในไฟล์เป็น NULL
)
LIMIT 5; -- แสดงเพียง 5 แถวแรก- สำรองฐานข้อมูลทั้งหมดลงโฟลเดอร์
backup/picha_2026ในรูปแบบ Parquet พร้อม compressionzstdจากนั้นเขียนคำสั่งสำหรับ restore ฐานข้อมูลกลับ
คลิกเพื่อดูเฉลย
-- ส่งออกฐานข้อมูลทั้งหมดเป็นไฟล์ Parquet พร้อม compression
EXPORT DATABASE 'backup/picha_2026' ( -- ส่งออกไปยัง folder ที่กำหนด
FORMAT parquet, -- บันทึกในรูปแบบ Parquet (ประหยัดพื้นที่และอ่านเร็ว)
COMPRESSION zstd -- บีบอัดด้วย zstd (ขนาดเล็ก ความเร็วสมดุล)
);
-- นำเข้าฐานข้อมูลที่ส่งออกไว้กลับมา (Restore)
IMPORT DATABASE 'backup/picha_2026'; -- อ่านไฟล์จาก folder เดิมที่ export ไว้