CodeGym /課程 /SQL SELF /資料反正規化的範例與其後果

資料反正規化的範例與其後果

SQL SELF
等級 25 , 課堂 4
開放

正規化解決了一些問題,但有時候也會帶來其他麻煩,尤其是效能方面。今天我們就要帶你進入這個有點黑暗(有時候也很光明)的技術領域——反正規化。沒錯,你可以違反正規化的規則……但要有腦袋地用啦!

反正規化就是跟正規化相反的過程。如果正規化是把資料表拆成不同的邏輯實體,盡量減少重複,那反正規化就是把資料再合併回來,為了提升效能。當系統負載很大、複雜查詢很頻繁時,連接一堆資料表會讓系統變慢,這時候反正規化就很常被用到。

可以說,反正規化就是在資料純淨度和查詢速度之間做個妥協。

什麼時候該用反正規化?

跟所有工具一樣,重點是要知道什麼時候用反正規化才對。通常會在以下這些情況下用:

  1. 常用查詢變慢了。 如果你的系統負載很大,常常跑一樣的查詢(像是彙總報表或 aggregate),連接一堆資料表會花很多時間。反正規化可以減少這種 join 的數量。

  2. 分析任務和統計。 在分析系統(像 BI — Business Intelligence)裡,常常要做大量資料分析。這時候反正規化可以靠「預先處理」好的資料來加速查詢。

  3. 複雜查詢。 如果你每次查詢都要 join 五張、十張甚至更多資料表,這會讓資料庫跑得超慢。反正規化可以讓查詢結構簡單一點。

  4. join 數量已經超過常理。 如果你的查詢有 25 張表在 JOIN,也許該重新思考一下設計了。

反正規化的範例

範例 1:網路商店。 在正規化的網路商店資料庫裡,可能會有這些資料表:

  1. customers — 客戶資料。
  2. orders — 訂單資訊。
  3. products — 商品資料。
  4. order_items — 訂單裡的商品。

查詢資料可能會像這樣:

SELECT
    c.customer_name,
    o.order_date,
    p.product_name,
    oi.quantity
FROM 
    customers c
JOIN 
    orders o ON c.customer_id = o.customer_id
JOIN 
    order_items oi ON o.order_id = oi.order_id
JOIN 
    products p ON oi.product_id = p.product_id
WHERE 
    c.customer_id = 42;

但如果你的網路商店一天要處理幾十萬筆訂單?這個查詢因為 join 太多表會變得超慢。

解法:反正規化。

我們可以建立一張專門放常用資訊的表:

CREATE TABLE order_summary AS
SELECT 
    c.customer_id,
    c.customer_name,
    o.order_id,
    o.order_date,
    p.product_id,
    p.product_name,
    oi.quantity
FROM 
    customers c
JOIN 
    orders o ON c.customer_id = o.customer_id
JOIN 
    order_items oi ON o.order_id = oi.order_id
JOIN 
    products p ON oi.product_id = p.product_id;

現在只要查資料,直接查 order_summary 就好:

SELECT * FROM order_summary WHERE customer_id = 42;

範例 2:分析系統。 假設你在一間賣活動門票的公司資料庫工作。有這些資料表:

  1. events — 活動資訊。
  2. sales — 門票銷售資料。

如果分析師要做一份每個活動平均票價的報表,正規化結構下你每次都得跑 aggregate 查詢:

SELECT
    e.event_name,
    AVG(s.price) AS avg_ticket_price
FROM 
    events e
JOIN 
    sales s ON e.event_id = s.event_id
GROUP BY 
    e.event_name;

這個查詢如果每筆銷售都佔一行,幾百萬筆資料會跑超慢。

解法:反正規化。 我們可以建立一張 aggregate 資料表:

CREATE TABLE event_summary AS
SELECT 
    e.event_id,
    e.event_name,
    COUNT(s.sale_id) AS ticket_count,
    SUM(s.price) AS total_revenue,
    AVG(s.price) AS avg_ticket_price
FROM 
    events e
JOIN 
    sales s ON e.event_id = s.event_id
GROUP BY 
    e.event_id, e.event_name;

這樣報表查詢就會快很多:

SELECT
    event_name, 
    avg_ticket_price 
FROM 
    event_summary;

反正規化的後果

反正規化當然可以讓查詢變快,但它不是什麼萬能法寶啦。你可能會遇到這些狀況:

第一,資料會重複存放。當同一份資料存在好幾個地方,資料庫大小會暴增,管理起來也更麻煩。

第二,資料更新會變得更複雜。想像一下,你有一份客戶資料在 customers 表,還有一份複製在 order_summary。如果客戶換名字或地址,你得記得兩邊都要更新。漏掉一邊就會出錯,因為資料不一致了。

第三,因為這種重複,很容易搞混或出錯。就像有好幾個版本的同一份文件,有時候根本搞不清楚哪個才是對的。

最後,這種資料庫要維護和擴充都比較麻煩。你可能要寫 trigger 或 script 來確保所有資料都同步。這對開發者來說就是額外的工作。

總之,反正規化是一種工具,要懂得它的優缺點再用,才不會踩雷啦!

2
任務
SQL SELF, 等級 25, 課堂 4
上鎖
根據現有資料建立一個去正規化的資料表
根據現有資料建立一個去正規化的資料表
1
問卷/小測驗
資料正規化,等級 25,課堂 4
未開放
資料正規化
資料正規化
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION