續前一篇【Oracle 全欄位搜尋技巧:我如何查出 GUID 藏在哪張表裡?】
👉 https://ithelp.ithome.com.tw/articles/10372967
當時我寫了一個 SQL,可以快速搜尋 GUID 是否存在於特定表的欄位。
但實際使用時,我踩到一個坑:Oracle 報錯ORA-00932: inconsistent datatypes: expected - got CLOB
。
這篇就記錄一下我怎麼發現問題,以及最終的解決方法。
Oracle 在不同版本中,對 CLOB
欄位使用 UPPER()
、=
這類字串函式或比較運算子,常常會報錯。這意味著我得找其他方法來比對字串。
REGEXP_LIKE
REGEXP_LIKE
可以吃CLOB
,也能直接做大小寫不敏感比對。調整後的查詢如下:
SELECT 'ACC_ROLE' AS table_name, 'CRMAN' AS column_name, COUNT(*) AS match_count
FROM ACC_ROLE
WHERE REGEXP_LIKE(CRMAN, '^UPDOWN$', 'i')
HAVING COUNT(*) > 0
UNION ALL
SELECT 'ACC_ROLE', 'MFMAN', COUNT(*)
FROM ACC_ROLE
WHERE REGEXP_LIKE(MFMAN, '^UPDOWN$', 'i')
HAVING COUNT(*) > 0
UNION ALL
SELECT 'ACC_ROLE', 'ROLE_TYPE', COUNT(*)
FROM ACC_ROLE
WHERE REGEXP_LIKE(ROLE_TYPE, '^UPDOWN$', 'i')
HAVING COUNT(*) > 0;
後來我想改比對 UUID,也只要換掉字串即可:
REGEXP_LIKE(CRMAN, '^520D701C-2322-49C7-AEC0-8111FF933DA0$', 'i')
DBMS_LOB.SUBSTR
若不想用正規表達式,也能透過 DBMS_LOB.SUBSTR
把 CLOB
轉成 VARCHAR2
來比對:
WHERE UPPER(DBMS_LOB.SUBSTR(CRMAN, 4000, 1)) = 'UPDOWN'
AND DBMS_LOB.GETLENGTH(CRMAN) = 6
這樣可以避免被截斷誤判,但仍有長度限制。
因為 SQL 中出現很多類似的條件,我想用 VSCode 的正則替換一次搞定。
原始程式碼:
UPPER(CRMAN) = 'UPDOWN'
希望替換成:
REGEXP_LIKE(CRMAN, '^UPDOWN$', 'i')
UPPER\(\s*(?:\w+\.)?(\w+)\s*\)\s*=\s*'([^']+)'
REGEXP_LIKE($1, '^$2$', 'i')
一開始我寫成
\1
,結果替換出來變成字面字串,後來才發現 VSCode
的捕捉組要用$1
、$2
。
方法 | 優點 | 缺點 |
---|---|---|
REGEXP_LIKE |
可處理 CLOB ,簡潔,支援大小寫不敏感 |
大數據下會全表掃描,效率較差 |
DBMS_LOB.SUBSTR |
不需正規表達式,比對更精確(可控制長度) | 需額外處理長度,程式較冗長 |
「在 Oracle 中,若
CLOB
欄位使用UPPER() = '字串'
會報
ORA-00932,該如何安全比對字串?可否提供REGEXP_LIKE
與DBMS_LOB.SUBSTR
的範例?」