直接撈線上資料庫不行嗎?OLTP 與 OLAP 的殘酷 SQL 對決

老闆同一個提問,直接查線上交易資料庫要串五張表、現場算金額;星形架構三個 JOIN 收工。用一場 SQL 對決看懂 OLTP 與 OLAP 的差異

分享
直接撈線上資料庫不行嗎?OLTP 與 OLAP 的殘酷 SQL 對決

看完上一篇星形架構的優勢後,很多沒有開發過資料倉儲或數據中台的人可能會問:

「我直接從現有的線上交易資料庫撈資料不行嗎?為什麼要這麼麻煩,重新建一套事實表和維度表?」

我在只有 OLTP 線上系統的架構下,肩負建立數據中台的任務。這類問題不斷被提起:我該怎麼視覺化地呈現與解釋?這個起心動念,讓我寫下這篇文章——讓兩種架構同台對決:老闆的同一個提問,各寫一段 SQL,看差在哪裡。

比結構:蜘蛛網 vs. 星星狀

先看這兩種架構在資料庫裡長什麼樣子:

  • OLTP(線上交易系統):為了確保每筆資料只存一次(正規化),資料表就像一張蜘蛛網,被拆得非常細碎。
  • OLAP(數據中台/分析系統):為了讓報表好查詢,資料表被重組成星形架構,所有分析角度都圍繞著中間的「事實表」。
同一筆訂單的兩種長相:OLTP 蜘蛛網八張表、橘色標出本次查詢的五張表路徑、灰虛線是其他關聯;OLAP 星形架構以銷售事實表為中心連接日期、客戶、商品維度
左邊蜘蛛網裡,橘色是「這一題」要走的五張表,灰虛線是下一題可能要走的其他路;右邊星星,數字在中間、分析角度圍在四周。

接著出題。這是一個每個月都會出現的真實商業提問:

🎯 老闆的提問:「2026 年 8 月,每個城市、每個商品類別的銷售金額是多少?」

我們來看看,兩位選手分別要怎麼寫這段 SQL。

選手一:星形架構的優雅解法(數據中台)

在數據中台中,事實表(Fact Table)與維度表(Dimension Table)已經預先整理好:

  • 銷售事實表(fact_sales):已經算好 amount,並備妥關聯鑰匙。
  • 日期維度(dim_date):含有年份(year)與月份(month)。
  • 客戶維度(dim_customer):含有城市(city)。
  • 商品維度(dim_product):含有類別(category)。

還記得上一篇「小明買咖啡豆」的那筆訂單嗎?它在數據中台長這樣:

銷售事實表 fact_sales(一筆訂單=一列數字)

date_idcustomer_idproduct_idamount
20260815C001P0011000

客戶維度 dim_customer

customer_idnamecity
C001小明台北

商品維度 dim_product

product_idnamecategory
P001咖啡豆食品

日期維度 dim_date

date_idyearmonth
2026081520268

💻 星形架構的 SQL 語法:

SELECT
    c.city,
    p.category,
    SUM(f.amount) AS total_sales_amount
FROM fact_sales f
JOIN dim_date d
    ON f.date_id = d.date_id
JOIN dim_customer c
    ON f.customer_id = c.customer_id
JOIN dim_product p
    ON f.product_id = p.product_id
WHERE d.year = 2026
  AND d.month = 8
GROUP BY
    c.city,
    p.category
ORDER BY
    c.city,
    p.category;

✅ 一句話理解:從 fact_sales 拿準備好的銷售金額,用三張維度表分別決定「時間、城市、商品類別」,然後直接加總。邏輯清晰,一目瞭然。

選手二:直接查線上系統的硬派解法(OLTP)

線上交易系統為了維持資料的一致性,通常採用高度「正規化」的設計。同一筆訂單,被拆進五張表:

  • customers(客戶表)
  • products(商品表)
  • product_categories(商品類別表,與商品分開)
  • orders(訂單主檔)
  • order_items(訂單明細)

線上系統有些時候沒有直接存 amount,金額必須現場計算:數量(quantity)× 單價(unit_price)。

小明那筆訂單,在線上系統裡長這樣:

customers(客戶表)

customer_idnamecity
C001小明台北

orders(訂單主檔)

order_idorder_datestatuscustomer_id
O0012026-08-15PAIDC001

order_items(訂單明細)

order_idproduct_idquantityunit_price
O001P0012500

products(商品表)

product_idnamecategory_id
P001咖啡豆CAT1

product_categories(商品類別表)

category_idcategory_name
CAT1食品

💻 正規化資料庫的 SQL 語法:

SELECT
    c.city,
    pc.category_name,
    SUM(oi.quantity * oi.unit_price) AS total_sales_amount
FROM orders o
JOIN customers c
    ON o.customer_id = c.customer_id
JOIN order_items oi
    ON o.order_id = oi.order_id
JOIN products p
    ON oi.product_id = p.product_id
JOIN product_categories pc
    ON p.category_id = pc.category_id
WHERE o.order_date >= DATE '2026-08-01'
  AND o.order_date < DATE '2026-09-01'
  AND o.status = 'PAID'
GROUP BY
    c.city,
    pc.category_name
ORDER BY
    c.city,
    pc.category_name;

你發現了嗎?同一個問題,這個查詢要多做三件事:

  • 把五張表串起來(JOIN 四次)
  • 自己處理時間區間的過濾,還要記得排除未付款(status = 'PAID')
  • 在查詢的當下現場計算營收(quantity × unit_price)

資深工程師看到這裡也許會說:「這段 SQL 我十分鐘就寫完了,哪裡複雜?」沒錯——複雜的從來不是寫一次,而是每張報表、每個 API 都重寫一次,而且各寫各的。

對決計分板:同一個老闆提問,星形架構 JOIN 三次且金額已算好,OLTP 要串五張表、現場計算金額並記得排除未付款
同一個提問,兩邊在「串幾張表、金額怎麼來、過濾條件、業務規則」四個項目的比分。

剛解題成功只是開端:老闆的下一個維度需求馬上就到

老闆看著報表說:「很好,那再加上『每個門市』。」

星形架構這邊,SQL 只改兩行——因為「門市」這個分析角度,早就是一張準備好的維度表(還記得上一篇星形圖裡的 dim_store 台北信義店嗎):

-- 原查詢只要加一個 JOIN、一個欄位
JOIN dim_store s
    ON f.store_id = s.store_id
...
GROUP BY c.city, p.category, s.store_name

OLTP 這邊呢?先想清楚 store_id 存在 orders 還是 order_items、門市名稱要再 JOIN 哪張表,然後重寫查詢、重新測試。

如果你把查詢包成了 API,這就是再開發一個新 endpoint(API 底下的一條路徑,對應一種查法)——

下週老闆說「改成看每週趨勢」「只看金卡會員」,每一句話,都是一個新的 endpoint。

一題一答 vs 一類題:左邊三個老闆需求各自變成一支新 API endpoint、各走一條蜘蛛網新路徑;右邊三個需求都指向同一顆星形架構,每次只換 GROUP BY
老闆每問一句,API 清單就長一支、蜘蛛網上多一條手刻的路;數據中台這邊,三個問題共用同一顆星,每次只是換一個 GROUP BY。

API 一題一答;維度建模一次建好,老闆之後的每一次追問,都只是換一個 GROUP BY。星形架構回答的不是「這一題」,是「這一類題」。

直接查線上資料庫的「五大災難」

小系統、臨時查詢直接查線上資料庫當然可以。但當企業規模變大、報表變多時,就會衍生以下痛點:

  1. JOIN 太多,查詢複雜易錯:報表開發者必須非常熟悉訂單主檔、明細、客戶、商品表的關聯。少 JOIN 一張或條件寫錯,數據就全錯了。
  2. 業務規則四處散落:「銷售金額」到底怎麼算?要扣折扣嗎?含稅嗎?排除退貨嗎?退貨要扣在下單那個月,還是退貨那個月?這一整組「到底怎麼算」的規則,如果每張報表、每個 API 都自己寫一遍 SUM(quantity * unit_price),最後全公司的數據一定對不起來——財務和業務儀表板報出兩個數字,兩邊 SQL 都沒寫錯,只是各自解讀了一套規則。
  3. 拖垮線上系統效能:線上資料庫(OLTP)的主要任務是處理客人下單、付款、更新庫存。讓報表在上面跑大量複雜的彙整與運算,非常容易導致線上交易系統卡頓甚至當機。
  4. 歷史分類規則難以追溯:咖啡豆今天屬於「食品」,明天改分類到「飲品沖泡」。報表到底要看交易當時的分類,還是現在的?如果線上資料庫沒有設計歷史對應、又沒有數據中台的歷史維度設計,這根本無解。
  5. AI 與 API 開發困難:當 AI 模型或 API 只是要調用一段簡單的銷售數據,背後卻得執行一大串複雜的 JOIN 和業務邏輯,既沒效率又難維護。

查 secondary、包 API、建 view:三個聰明方案為什麼都差一步?

有經驗的工程師還會提出一個聰明的替代方案:「我查唯讀副本(read replica)就好了啊——或者我們公司已經做了 primary/secondary 讀寫分離,報表都查 secondary,不就不會拖垮線上系統了嗎?」

沒錯,副本確實解掉了第三個災難——分析負載不再打到線上主庫。但也只有這一項。副本上的資料仍然是那張蜘蛛網:JOIN 還是一樣多、業務規則還是散落在每張報表裡、歷史狀態還是無法追溯。Replica 複製的是資料,不是模型。

第二個方案更進一步:「那我把常用查詢包成 API,統一打在 secondary 上,業務規則不就集中了嗎?」——API 集中化的是程式碼,不是資料模型。每一個新的分析角度、每一次老闆改需求,都是一個新 endpoint,資料 API 團隊很快就會變成全公司取數的排隊窗口。

這裡的差別,其實是「什麼時候算」:

差異點API(蓋在 OLTP 上)數據中台
規則什麼時候套查詢當下,每查一次算一次排程時算好一次(常見 T+1),所有人共用
查詢當下在做什麼找表 → JOIN → 套規則 → 算金額,全包挑維度 → 加總
規則放在哪各個 endpoint 的程式碼裡,各寫各的事實表欄位裡(金額已算好),寫死一次

API 把「算」這件事留在每一次查詢;數據中台把它提前到排程,一次算完給所有人用。

什麼時候算的一日對照:數據中台在凌晨 ETL 跑一次重活(串五張表、套規則、算好金額),白天 09:00/11:00/15:00 三次查詢都只是挑維度加總;直接查 OLTP+API 凌晨沒人先算,白天三次查詢各自整串重算一遍,全落在營業時間
中台把最重的那一段(串表、套規則、算金額)挪到凌晨做一次,白天每次查詢都只剩加總;直接查 OLTP 則是每問一次就整串重算一次,而且全撞在掛號、批價正忙的時段。

第三個方案最接近答案:「我用 view 來撰寫,把 JOIN 和計算規則寫在裡面呢?」——view 確實把邏輯集中了,但每次查詢仍然要在線上引擎現場算那一串 JOIN;如果你的資料庫為了效能開始建 materialized view(把查詢結果先算好、存成實體表)、加排程刷新,恭喜,你已經在手工打造一座小型的數據中台了。

Replica 解決「在哪裡查」,API 解決「誰來寫」,view 解決「寫在哪」——只有維度建模解決「還要再寫幾次」。

你會發現,這三個聰明的替代方案每多走一步,離數據中台就更近一步。差別只在,有沒有把它當成正式工程來蓋(版本控制、依賴管理、測試、歷史設計)。

問題從來不在 API 本身。對外交付數據,最後往往還是走 API——真正的差別在 API 蓋在哪裡。

蓋在 OLTP 蜘蛛網上,每個 endpoint 都要手刻一條 JOIN 路徑、規則各解讀各的;蓋在數據中台上,事實表與維度表已經備妥,endpoint 只剩下「組裝」——選維度、加總、輸出,又薄又快,全公司的算法還一致。

數據中台不是取代 API,是讓每一支 API 從手刻變組裝。

你們公司的報表現在是怎麼查的——直接查主庫、查讀寫分離的 secondary,還是已經有獨立的資料倉儲/數據中台?歡迎留言或回信告訴我,後續能寫文章回答大家的提問。

總結:兩者的核心差異比較

面向直接查線上資料庫(OLTP,正規化)星形架構(OLAP,事實表+維度表)
主要目的支援交易系統,確保寫入與更新正確支援分析與報表,確保查詢直覺快速
查詢方式大量交易表與細節表互相 JOIN單一事實表 JOIN 幾張維度表
業務規則寫在報表 SQL 裡,由開發者各自解讀在數據中台/倉儲集中處理與定義
指標計算查詢時「現場」計算(如:數量 × 單價)事實表中通常已先「標準化」算好
效能風險跑大報表極易拖垮線上交易系統分析負載與交易系統分離,互不干擾
資料即時性即時,查到的就是當下狀態依排程批次更新(常見 T+1),略有延遲
歷史管理較難處理分類與主檔變更的問題可利用「維度版本」輕鬆追溯歷史狀態
供 AI/API 使用架構極度複雜,容易導致規則不一致架構穩定、語意清晰,隨取即用
💡 一句話總結:事實表、維度表與星形架構的真正價值,就是把複雜的「清理、關聯、計算」工作在數據中台先做掉,讓後面的報表、API、AI 不用每次都從交易資料庫重新痛苦地拆解一次。

當然,數據中台也不是免費的午餐——要投入多少人力、什麼時候才值得建,是另一個完整的題目,之後再來談。

那咖啡豆改了分類、客戶搬家換了城市,報表到底該看「當時」還是「現在」?線上資料庫系統會怎麼設計?如果沒有這樣的設計,數據中台有沒有辦法解?下一篇,我們再來分析。


延伸閱讀

如果你也走在自己的路上,歡迎訂閱《小穗步電子報》——我會把每一次的覺察與練習,寫成信寄給你。→ 訂閱小穗步電子報

Read more