Oracle 判斷字符串是否為數值的自定義函數:TRANSLATE 技巧實現
Oracle 無內建 ISNUMERIC() 函數,常用 TRANSLATE 技巧實現高效能數值判斷。
核心函數代碼
CREATE OR REPLACE FUNCTION isnumeric (p_str IN VARCHAR2)
RETURN NUMBER
IS
v_str VARCHAR2(1000);
BEGIN
IF p_str IS NULL THEN
RETURN 0;
ELSE
-- 關鍵技巧:將所有數字字符與小數點替換為單一字符 '.'
v_str := TRANSLATE(p_str, '.0123456789', '.');
-- 合法數值轉換後僅剩 '.' 或 '+.' 或 '-.' 或空字串
IF v_str = '.'
OR v_str = '+.'
OR v_str = '-.'
OR v_str IS NULL THEN
RETURN 1; -- 是數值
ELSE
RETURN 0; -- 非數值
END IF;
END IF;
END isnumeric;
/
TRANSLATE 函數機制深度解析
語法
TRANSLATE(string, from_str, to_str)
from_str 中的每個字符對應替換為 to_str 相同位置的字符
- 若
from_str 比 to_str 長,多餘字符在結果中被刪除
to_str 不能為空 (空字串視為 NULL,導致結果為 NULL)
本函數的替換邏輯
| from_str (來源字符集) | to_str (目標字符集) | 結果 |
| . 0 1 2 3 4 5 6 7 8 9 (11 字符) | . (1 字符) | 所有數字與小數點 → '.';其他字符 → 刪除 |
轉換結果範例
| 輸入字串 | TRANSLATE 結果 | 判斷 | 說明 |
| '123.45' | '......' | 非數值 ❌ | 多個小數點 |
| '123' | '...' | 非數值 ❌ | 純整數但邏輯判斷為非數值 (見下文修正) |
| '.123' | '....' | 非數值 ❌ | |
| '-123.45' | '-......' | 非數值 ❌ | 負號保留 |
| '+123.45' | '+......' | 非數值 ❌ | 正號保留 |
| '123abc' | '...abc' | 非數值 ❌ | 字母保留 |
⚠️ 原函數邏輯缺陷與修正版
原代碼判斷邏輯 v_str = '.' OR v_str = '+.' OR v_str = '-.' OR v_str IS NULL 存在缺陷:
- 純整數 '123' → '...' 不匹配任何條件 → 返回 0 (誤判)
- 多個小數點 '1.2.3' → '....' 不匹配 → 返回 0 (正確但非預期原因)
修正版:支援整數、小數、科學記號、正負號
CREATE OR REPLACE FUNCTION isnumeric_v2 (p_str IN VARCHAR2)
RETURN NUMBER
IS
v_clean VARCHAR2(1000);
BEGIN
IF p_str IS NULL THEN
RETURN 0;
END IF;
-- 1. 移除前後空白
v_clean := TRIM(p_str);
-- 2. 正則表達式驗證 (10g+ 推薦,最精確)
-- 模式:可選正負號 + 數字 + 可選小數部 + 可選指數部
IF REGEXP_LIKE(v_clean, '^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$') THEN
RETURN 1;
END IF;
RETURN 0;
END isnumeric_v2;
/
TRANSLATE 經典應用範例
-- 1. 字符替換映射
SELECT TRANSLATE('abcdefghij', 'abcdef', '123456') FROM dual;
-- 結果: '123456ghij' (a→1, b→2, c→3, d→4, e→5, f→6)
-- 2. 目標字符集較短:多餘字符被刪除
SELECT TRANSLATE('abcdefghij', 'abcdefghij', '123456') FROM dual;
-- 結果: '123456' (g,h,i,j 在 from_str 有對應但 to_str 無對應 → 刪除)
-- 3. 刪除特定字符 (to_str 為單一字符)
SELECT TRANSLATE('abcbbaadef', 'ba', '#@') FROM dual;
-- 結果: '@#cc@@@def' (b→#, a→@)
-- 4. 刪除字符:from_str 有字符但 to_str 無對應
SELECT TRANSLATE('abcbbaadef', 'bad', '#@') FROM dual;
-- 結果: '@#cc@@@ef' (b→#, a→@, d→刪除)
效能對比
| 方法 | 效能 | 精確度 | 版本需求 |
| TRANSLATE 技巧 | 極高 (純字符操作) | 中 (需處理邊界情況) | 所有版本 |
| REGEXP_LIKE | 中 (正則引擎開銷) | 極高 (標準模式) | 10g+ |
| TO_NUMBER + EXCEPTION | 低 (異常處理開銷大) | 高 | 所有版本 |
使用建議
- 高頻呼叫/大量資料:使用修正版 TRANSLATE 邏輯或預先建立函數索引
- 精確度要求高:優先使用
REGEXP_LIKE (10g+)
- 避免:
TO_NUMBER 捕獲異常的方式,效能極差
- 12c+:可考慮虛擬欄位 + 函數索引優化查詢