iT邦幫忙

2026 iThome 鐵人賽

DAY 1
2

前言

提到資料工程,你最先想到的可能是 ETL:

  • 從 API、資料庫或檔案擷取資料
  • 清理缺失值與錯誤格式
  • 轉換欄位型別
  • 將資料寫入 Data Warehouse 或 Data Lake
  • 使用排程定期執行資料處理流程

但資料工程真的只是「把資料從一個地方搬到另一個地方」嗎?

假設我們有一份訂單資料,現在想計算 2026 年截至今天(2026 年 8 月 17 日),每個城市的訂單總金額:

SELECT city, SUM(amount) AS total_amount
FROM orders
WHERE order_date BETWEEN DATE '2026-01-01'
                     AND DATE '2026-08-17'
GROUP BY city;

對使用者來說,我們只送出了一段 SQL。

但從 SQL 送出到結果顯示以前,系統還要經過解析、規劃、最佳化與實際運算。

負責這些工作的核心元件,就是今天要認識的 Query Engine(查詢引擎)


資料工程不只是 ETL

ETL 分別代表:

Extract → Transform → Load
擷取資料 → 轉換資料 → 載入資料

三個階段各自負責:

  • Extract:從資料庫、API、CSV、JSON 等來源取得資料
  • Transform:清理、篩選、計算、整合或重新組織資料
  • Load:將結果寫入目標資料庫、Data Warehouse 或檔案

Transform 常見的操作包括:

  • 使用 WHERE 篩選資料
  • 保留真正需要的欄位
  • 計算新的衍生欄位
  • 使用 JOIN 整合不同資料集
  • 使用 GROUP BY 進行分組
  • 使用 SUMCOUNTAVG 產生彙總結果

不過,ETL 階段和 SQL 語法並不是一對一對應。

例如,SELECT * FROM orders 可以用來從來源取得資料,因此能視為 Extract;如果 SELECT 中還包含欄位計算、篩選或聚合,它也同時參與了 Transform。

換句話說,SQL 描述的是資料操作方式,Extract、Transform、Load 描述的則是這些操作在整個資料流程中扮演的角色。

一套資料工程 Pipeline 通常需要處理:

資料工程 Pipeline
├── 資料從哪裡來
├── 需要經過哪些轉換
├── 何時執行
├── 失敗時如何處理
└── 結果要寫到哪裡

Query Engine 的角色,則是理解 SQL 描述的需求,將篩選、欄位選擇、聚合與 JOIN 等操作,轉換成電腦可以執行的資料運算流程。

Query Engine 也不只存在於傳統資料庫中。它可以被嵌入資料處理工具、DataFrame 函式庫、串流系統,以及其他需要分析資料的應用程式。


什麼是 Query Engine?

Query Engine 可以先簡單理解成:

把使用者描述的查詢需求,轉換成實際可執行的資料處理流程。

以剛才的 SQL 為例,Query Engine 必須理解:

  1. 資料來源是 orders
  2. 篩選指定日期範圍內的資料
  3. 使用 city 分組
  4. 計算每組的 SUM(amount)
  5. 輸出 citytotal_amount

它不只要知道「要做什麼」,還要決定「實際怎麼做」。

一段 SQL 從輸入到產生結果,可以先簡化成:

SQL
    ↓
SQL Parsing
    ↓
Logical Planning ← Data Source / Schema
    ↓
Logical Optimization
    ↓
Physical Planning
    ↓
Physical Optimization
    ↓
Execution → 讀取 Data Source
    ↓
Result

接下來看看每個階段負責什麼。


第一站:解析 SQL

SQL 對人類來說是一段文字,但 Query Engine 不會直接拿著字串開始計算。

它會先進行 SQL Parsing,辨認其中的資料表、欄位、函數、條件與運算子。

例如:

SELECT
├── city
└── SUM(amount)

FROM
└── orders

WHERE
└── order_date BETWEEN ...

GROUP BY
└── city

如果把 SELECT 寫成 SELEC,這類語法錯誤通常也會在解析階段被發現。


第二站:建立 Logical Plan

SQL 解析完成後,Query Engine 會建立 Logical Plan

Logical Plan 描述的是:

這段查詢在邏輯上需要完成哪些操作?

剛才的 SQL 可以簡化成:

Projection: city, total_amount
    ↓
Aggregate: GROUP BY city, SUM(amount)
    ↓
Filter: order_date BETWEEN ...
    ↓
TableScan: orders

這棵計畫可以由下往上閱讀:

  1. 掃描 orders
  2. 篩選指定日期範圍內的資料
  3. 按照 city 分組並計算總金額
  4. 輸出需要的欄位

建立及驗證 Logical Plan 時,Query Engine 需要找到 orders 對應的資料來源,並取得它的 Schema:

orders
├── order_id: Int64
├── city: Utf8
├── amount: Float64
└── order_date: Date32

有了 Schema,系統才能檢查欄位是否存在、資料型別是否能進行指定運算,以及查詢結果應該包含哪些欄位。

此時主要是在規劃查詢,真正大量讀取資料內容通常會在執行階段發生。


第三站:最佳化 Logical Plan

同一個查詢結果,可能有不同的執行方式。

Optimizer(查詢最佳化器)會嘗試在不改變查詢結果的前提下,減少不必要的工作。

例如,orders 可能有許多欄位,但這段 SQL 實際只需要:

city
amount
order_date

Query Engine 可以嘗試避免讀取其他欄位。這類最佳化稱為 Projection Pushdown,也常稱為欄位裁剪。

WHERE 條件也可能被移動到更接近資料來源的位置,讓不符合條件的資料儘早被排除。這稱為 Filter Pushdown

讀取資料
    ↓
儘早篩選
    ↓
只處理剩餘資料

現在只要先記住:

最佳化不是改變 SQL 的答案,而是在維持相同結果的前提下,尋找更適合的執行方式。

我們會在 Day 16~20 進一步觀察這些最佳化規則。


第四站:建立 Physical Plan

Logical Plan 描述「要做什麼」,Physical Plan 則描述:

實際上要怎麼執行?

到了這個階段,Query Engine 會考慮更多執行細節,例如:

  • 使用哪些執行節點
  • 資料如何分區
  • 是否能平行處理
  • 不同節點之間如何傳遞資料
  • JOIN 或 Aggregate 使用哪種執行方式

簡化後的 Physical Plan 可能像這樣:

ProjectionExec
    ↓
AggregateExec
    ↓
FilterExec
    ↓
DataSourceExec

其中的 Exec 可以先理解為負責實際執行某項操作的元件。

真實的 Physical Plan 可能包含更多節點,但 Day 1 先掌握 Logical Plan 與 Physical Plan 的角色差異即可。


第五站:執行並產生結果

Physical Plan 準備完成後,Query Engine 才真正開始讀取與處理資料。

大致流程是:

  1. 從資料來源讀取一批資料
  2. 執行 Filter
  3. 將符合條件的資料送進 Aggregate
  4. 計算每個城市的總金額
  5. 產生查詢結果

Apache DataFusion 使用 Apache Arrow 的欄式記憶體格式。執行過程中,資料會以一批一批的 RecordBatch 在不同執行節點之間傳遞。

最後得到的結果可能是:

+-----------+--------------+
| city      | total_amount |
+-----------+--------------+
| Taipei    | 152000       |
| Taoyuan   | 98000        |
| Kaohsiung | 76000        |
+-----------+--------------+

結果除了顯示在畫面上,也可以:

  • 寫入 CSV
  • 寫入 Parquet
  • 交給應用程式繼續處理
  • 成為下一段資料 Pipeline 的輸入

為什麼資料工程師要理解 Query Engine?

理解 Query Engine,可以幫助我們回答:

  • 為什麼兩段結果相同的 SQL,速度可能不同?
  • 為什麼 Parquet 通常比 CSV 更適合分析?
  • WHERE 條件有沒有提早套用?
  • 查詢是否讀取了不需要的欄位?
  • EXPLAIN 顯示的 Query Plan 代表什麼?
  • SQL 最後如何變成真正的運算?

這些問題同時涉及資料格式、查詢設計、系統效能與資源使用,也是資料工程不只停留在「搬運資料」的重要原因。


今日小結

今天先建立了一段 SQL 的生命週期:

SQL
    ↓
SQL Parsing
    ↓
Logical Plan
    ↓
Logical Optimization
    ↓
Physical Plan
    ↓
Physical Optimization
    ↓
Execution
    ↓
Result

目前只要先掌握三件事:

  1. SQL 不會直接變成結果,中間需要經過規劃與執行。
  2. Logical Plan 描述要做什麼,Physical Plan 描述實際怎麼做。
  3. Optimizer 會在結果不變的前提下,嘗試減少不必要的運算。

接下來 30 天,我們會使用 Rust 與 Apache DataFusion,逐步拆解這條流程,最後完成一套可以載入 CSV、JSON、Parquet,執行 SQL、查看 Query Plan 並輸出結果的本機資料分析工具。

下一篇將先回答:

為什麼選擇 Rust 與 Apache DataFusion?


延伸閱讀


下一篇
Day 2|為什麼是 Rust × Apache DataFusion?
系列文
深入 SQL 查詢引擎:30 天用 Rust 與 Apache DataFusion 解構資料處理流程2
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Wolke
iT邦研究生 4 級 ‧ 2026-08-17 03:04:13

把 SQL 先拆成 Parsing、Logical Plan、Physical Plan 再到 Execution,整條路徑一下就有呼吸感了,尤其 Projection Pushdown 跟 Filter Pushdown 這種「先少做一點」的味道,真的很像在替查詢省力。還有 DataFusion 用 RecordBatch 在節點間傳資料,那個欄式記憶體的畫面很清楚,也讓人更懂 EXPLAIN 背後在看什麼;這系列也很自然讓我想到 AI 開發時把流程拆細後,實作門檻會低很多。我手邊有多的 Lovable 額度想送給有緣人,有興趣可從連結看看我的系列。 https://ithelp.ithome.com.tw/articles/10401174

我要留言

立即登入留言