本文探討一個常見的程式設計陷阱:在條件判斷式中忽略 NULL 值的處理,以及如何透過「成功測試」的思維來避免這類問題。

問題根源:NULL 在條件判斷中的行為

根據 Genero 文件的說明:「NULL 運算式在 IF 陳述式中被視為 FALSE」。此外,「布林運算式的結果為 TRUE 或 FALSE,但若其中一個運算元為 NULL,結果也可能是 NULL。」

這就形成了一個陷阱——當我們測試「失敗條件」(例如 IF expr NOT MATCHES "A*")時,NULL 值會讓整個條件評估為 FALSE,導致錯誤訊息不顯示,讓不合法的情況悄悄通過。

四種測試方式比較

方式一:錯誤的失敗測試(Bad Test for Failure)

直接測試否定條件,未處理 NULL,會漏掉 NULL 的情況:

IF expr NOT MATCHES "A*" THEN
    DISPLAY "Value must begin with A"
END IF

expr 為 NULL,條件評估為 FALSE,錯誤訊息不會顯示。

方式二:正確的失敗測試(Correct Test for Failure)

明確加上 IS NULL 的判斷:

IF expr IS NULL OR expr NOT MATCHES "A*" THEN
    DISPLAY "Value must begin with A"
END IF

方式三:使用 nvl() 的失敗測試(NVL Test for Failure)

利用 nvl() 函數在值為 NULL 時替換為預設值:

IF nvl(expr," ") NOT MATCHES "A*" THEN
    DISPLAY "Value must begin with A"
END IF

方式四:成功測試(Test for Success)— 推薦做法

反轉邏輯,改為測試「正確條件」,將所有未滿足的情況(包括 NULL)導向錯誤處理:

IF expr MATCHES "A*" THEN
    # 值符合要求,繼續執行
ELSE
    DISPLAY "Value must begin with A"
END IF

推薦策略:以成功為前提設計測試

作者建議採用「成功測試」的思維,也就是測試「條件成立時」應該發生什麼,而非測試「條件不成立時」。這樣的設計天然地將所有非預期情況(包括 NULL)導向錯誤處理路徑,無需為每個變數都額外撰寫 NULL 判斷。

實際案例

原始的問題程式碼:

IF get_version() < 1974 THEN
   # 版本不足,停止執行
END IF

get_version() 回傳 NULL 時,NULL < 1974 評估為 FALSE,程式未能停止,繼續執行導致後續錯誤。

改用成功測試後:

IF get_version() >= 1974 THEN
    # 版本符合要求,繼續執行
ELSE
   # 版本不足或為 NULL,停止執行
END IF

現在,無論 get_version() 回傳 NULL 還是過舊的版本號,都會正確地進入 ELSE 分支進行處理。

重點摘要

  • NULL 在 IF 條件中被視為 FALSE,「測試失敗」的寫法容易漏掉 NULL 情況。
  • 「測試成功」的寫法(IF 正確條件 THEN ... ELSE 錯誤處理)可自然涵蓋 NULL。
  • 若必須使用失敗測試,可搭配 IS NULL ORnvl() 明確處理 NULL。
  • 養成「以成功為前提」的撰碼習慣,可大幅減少因 NULL 導致的隱性 Bug。
原文:https://4js.com/ask-reuben/ig-87/  ·  有任何 Genero 技術問題或翻譯疑問,歡迎來信 support@t100.app