本文探討一個常見的程式設計陷阱:在條件判斷式中忽略 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 OR或nvl()明確處理 NULL。 - 養成「以成功為前提」的撰碼習慣,可大幅減少因 NULL 導致的隱性 Bug。
原文:https://4js.com/ask-reuben/ig-87/
· 有任何 Genero 技術問題或翻譯疑問,歡迎來信
support@t100.app