CodeGym /課程 /SQL SELF /在真實場景中使用 transaction 的範例

在真實場景中使用 transaction 的範例

SQL SELF
等級 39 , 課堂 3
開放

有時候 transaction 就像超級英雄電影裡的角色一樣,能在故障、錯誤或系統出包時拯救我們的資料庫。如果你在處理一個需要多個操作、而且不能拆開來做的任務,transaction 就能確保這些操作會一起完成。我們來看看處理付款時,transaction 是怎麼發揮作用的。

處理付款

想像一下經典情境:你有兩個銀行帳戶,想把錢從一個帳戶轉到另一個。這可不是「按一下按鈕」那麼簡單。我們得確保從一個帳戶扣款、另一個帳戶加款都正確。任何一個步驟出錯都可能很慘:要嘛兩個帳戶都沒變,要嘛餘額亂掉(像是錢突然消失或「憑空出現」)。

情境:帳戶間轉帳

來看一下我們的程式碼。要像讀來自遙遠銀河的訊息一樣仔細看:

-- 開始 transaction
BEGIN;

-- 步驟 1. 從發送者帳戶扣款
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;

-- 步驟 2. 把錢加到收款者帳戶
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;

-- 一切順利?那就存檔!
COMMIT;

這裡重點是什麼?

  • 如果在 步驟 1步驟 2 有什麼出錯(像是 SQL 寫錯、錢不夠),transaction 可以用 ROLLBACK 來「倒帶」,資料就會回到原本的狀態。
  • COMMIT 保證只有所有步驟都成功才會真的寫進資料庫。

加上餘額檢查

那如果發送者的錢不夠轉呢?我們來加個餘額檢查,避免讓他「負債」。

-- 開始 transaction
BEGIN;

-- 取得發送者目前餘額
DO $$
DECLARE
    current_balance NUMERIC;
BEGIN
    SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;

    -- 檢查錢夠不夠
    IF current_balance >= 100 THEN
        -- 錢夠就轉帳
        UPDATE accounts
        SET balance = balance - 100
        WHERE account_id = 1;

        UPDATE accounts
        SET balance = balance + 100
        WHERE account_id = 2;

        -- 存檔
        COMMIT;
    ELSE
        -- 錢不夠就倒帶
        ROLLBACK;
        RAISE NOTICE '轉帳金額不足!';
    END IF;
END $$;

這裡有什麼有趣的?

  • 我們用 PL/pgSQL 的區塊,透過 IF 來檢查條件。如果餘額小於要轉的金額,transaction 會被拒絕,什麼都不會變。
  • ROLLBACK 會取消已經做的變更(雖然這裡還沒動到資料,但這樣寫比較保險)。
重點!

這堂課是講 transaction 的真實應用場景,所以我特地舉了一個生活中的例子。這裡有 stored procedure,用 PL-SQL 寫的。我覺得你們已經有足夠經驗,應該看得懂這怎麼運作。以後我們還會回來講 PL-SQL,甚至會看更複雜的例子。

在 transaction 裡批次更新資料

transaction 不只用在轉帳這種情境。假設我們有個電商資料庫,每天有很多訂單狀態會變,比如從「運送中」變成「已完成」。要怎麼一次更新很多筆資料,而且如果出錯還能全部倒回去?當然就是用 transaction 啦!

再來看一個情境:更新訂單狀態。

範例如下:

-- 開始 transaction
BEGIN;

-- 步驟 1. 更新已經過期的訂單
UPDATE orders
SET status = 'completed'
WHERE delivery_date < CURRENT_DATE;

-- 步驟 2. 通知更新成功
RAISE NOTICE '所有訂單狀態都更新成功啦。';

-- 實際寫入
COMMIT;

如果出錯怎麼辦?

總是有可能出錯。比如你不小心忘了寫 WHERE 條件,結果所有訂單都變成 completed。為了避免這種事,記得要結束 transaction 或明確倒帶。

來看倒帶的情境:

-- 開始 transaction
BEGIN;

-- 步驟 1. 嘗試更新訂單但沒寫條件(糟糕,出錯了!)
UPDATE orders
SET status = 'completed';

-- 因為錯誤倒帶
ROLLBACK;

-- 現在訂單都沒被改到

SAVEPOINT 增加一點「彈性」

有時候你不想整個 transaction 都倒帶。如果你的流程有好幾個步驟,可能只想倒回其中一個。這時 SAVEPOINT 就派上用場啦!

現在我們的情境是:多步驟處理,但只想倒帶其中一個步驟。

想像你在處理一個訂單,有幾個步驟:從倉庫扣貨、更新訂單狀態、通知客戶。如果通知沒發成功,你只想倒帶這個步驟,但其他資料要保留。

-- 開始 transaction
BEGIN;

-- 步驟 1. 從倉庫扣貨
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;

-- 設定還原點
SAVEPOINT step1;

-- 步驟 2. 更新訂單狀態
UPDATE orders
SET status = 'shipped'
WHERE order_id = 202;

-- 嘗試通知客戶
SAVEPOINT step2;
-- 哎呀,通知出錯!
ROLLBACK TO SAVEPOINT step2;

-- 決定還是安全結束 transaction
COMMIT;

結論

transaction 不只是技術工具,更是你資料完整性的保證。它們能防止「骨牌效應」,避免一個錯誤毀掉整個系統。每次你要做多個相關操作時,問問自己:「如果其中一個失敗會怎樣?」如果答案是「災難」,那就該用 transaction 了。 記住:花幾分鐘寫 transaction,總比花幾小時救資料來得划算。你的使用者(還有你的心情)都會感謝你!

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION