CodeGym /課程 /SQL SELF /PL/pgSQL 除錯入門

PL/pgSQL 除錯入門

SQL SELF
等級 55 , 課堂 0
開放

想像一下,你寫了一個超複雜的 function 或 procedure。你已經看到你的資料庫跑得超順,結果突然——砰!——資料怪怪的、查詢變慢,老闆開始緊張。這時候,除錯就要上場啦。

PL/pgSQL 的除錯主要是為了:

  • 找出邏輯錯誤,比如 function 回傳的東西跟你預期的不一樣。
  • 搞懂輸入資料有問題。因為有時候資料庫的使用者不只會輸入資料,還會...輸入一些完全看不懂的東西!
  • 解決效能問題。畢竟趕工寫出來的 code,跑起來可能像在沙漠裡找 Wi-Fi 的烏龜一樣慢。

認真說,除錯不只是找 bug 跟修 bug。這也是讓你的 code 更快、更有效率、更好讀的方法。

PL/pgSQL 除錯的基本方法

PL/pgSQL 的除錯有幾種方式。我們一個一個來看。

  1. 用 PostgreSQL 內建工具

PostgreSQL 有幾個內建的診斷功能,包括 logging function(RAISE NOTICERAISE EXCEPTION),還有查詢執行計畫分析(EXPLAIN ANALYZE)。這些工具可以幫你搞懂 function 裡面到底發生什麼事。

  1. RAISE NOTICE 來 logging

RAISE NOTICE 就像你的好朋友,想知道 function 裡面資料怎麼流動、哪個階段出問題、或是檢查變數值,都靠它。跟 RAISE EXCEPTION 不一樣,它不會中斷 function 執行。比如說,你可以在每個階段都 print 出變數內容。

DO $$
DECLARE
    counter INT := 0;
BEGIN
    FOR counter IN 1..5 LOOP
        RAISE NOTICE '目前計數器的值: %', counter;
    END LOOP;
END $$;

這段 code 會 print 出 counter 從 1 到 5 的值。超簡單但超實用,除錯必備!

  1. 用外部工具

PL/pgSQL 除錯也可以靠像 pgAdmin 這種工具(有 GUI 介面)。你可以設 breakpoint、即時看變數值。如果你喜歡視覺化的工具,pgAdmin 絕對是你的好夥伴。

除錯的步驟

開始除錯 function 或 procedure 時,照著一定的順序來很重要。每個步驟我們都來細講一下:

  1. 分析輸入資料

第一步就是搞清楚輸入資料。要確定 function 拿到的資料沒有錯誤或奇怪的值。比如說,你可以用 RAISE NOTICE 檢查所有輸入參數:

CREATE FUNCTION check_input(x INTEGER) RETURNS VOID AS $$
BEGIN
    IF x IS NULL THEN
        RAISE EXCEPTION '輸入值不能是 NULL!';
    END IF;
    RAISE NOTICE '輸入值: %', x;
END;
$$ LANGUAGE plpgsql;

這個例子就是提醒使用者輸入資料有問題時要注意。

  1. 檢查每個階段的執行情況

把 function 拆成幾個邏輯區塊,在關鍵點加上 RAISE NOTICE。這樣你就能知道哪裡出錯。

CREATE FUNCTION calculate_discount(price NUMERIC, discount NUMERIC) RETURNS NUMERIC AS $$
BEGIN
    RAISE NOTICE 'Function 開始: 價格 %, 折扣 %', price, discount;

    IF price <= 0 THEN
        RAISE EXCEPTION '價格不能是負的或等於零!';
    END IF;

    IF discount < 0 OR discount > 100 THEN
        RAISE EXCEPTION '折扣必須在 0 到 100 之間!';
    END IF;

    RETURN price - (price * discount / 100);
END;
$$ LANGUAGE plpgsql;

這裡每個除錯階段都會 print 出有用的狀態訊息。

  1. 優化和解決問題
  2. 找到 bug 之後就修掉。如果是效能問題,用像 EXPLAIN ANALYZE 這種分析工具來優化查詢。

除錯技能的實戰應用

來看個實際例子:我們有個 function 會新增一筆資料到資料表,然後回傳產生的 id。看起來很簡單,但有時 function 會出錯,我們想知道為什麼。

原始 function:

CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
    new_id INTEGER;
BEGIN
    INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;
    RETURN new_id;
END;
$$ LANGUAGE plpgsql;

如果你用錯誤的資料呼叫這個 function,比如 age < 0,它就會出錯。我們來用除錯工具把它變更好。

加上 logging 的進階 function:

CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
    new_id INTEGER;
BEGIN
    -- logging 輸入資料
    RAISE NOTICE '新增學生: 名字 %, 年齡 %', name, age;

    -- 檢查年齡正確性
    IF age < 0 THEN
        RAISE EXCEPTION '年齡不能是負的!';
    END IF;

    -- 新增學生並回傳 ID
    INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;

    -- logging 成功訊息
    RAISE NOTICE '學生已新增,ID %', new_id;

    RETURN new_id;
END;
$$ LANGUAGE plpgsql;

現在如果出錯,你就能靠 RAISE NOTICE 的訊息馬上知道哪裡有問題。

最後給你的小建議

  1. 記得把不需要的 logging 拿掉。 RAISE NOTICE 超好用,但如果一直留在 production,log 會爆炸。
  2. 分段寫 code。 如果 function 太複雜,就拆成幾個小 function。這樣除錯超輕鬆。
  3. 多練習。 你寫 code 跟除錯越多,找 bug 跟修 bug 就越快。

除錯就像玩偵探遊戲,只是你拿的是 SQL 查詢跟 PL/pgSQL 邏輯,不是放大鏡。這種功力是靠經驗累積的,每修一個 bug,你就更強一點!

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