🔎EF Core 連 Oracle 出現 ORA-00600 [kpp_concatq:2] 的完整排錯指南(含 EF Core ToString/CultureInfo 錯誤)
摘要(Meta Description)
在 .NET EF Core 對 Oracle 查詢時,若遭遇
ORA-00600
[kpp_concatq:2],多半是查詢優化器在字串型別轉換或重寫(Query Transformation)時踩到版本
Bug。本文以實戰日誌形式,示範如何用
FromSqlRaw + 參數型別/長度對齊 + CAST + Hint
立即止血,並處理 EF Core 另一個常見錯誤:「CultureInfo 常值被傳進
ToString
造成可能記憶體外洩」。附可直接套用的程式碼。
目錄
錯誤現象
常見堆疊片段如下(節錄):
在修正過程中,亦可能遇到 EF Core 自己丟的錯(與 Oracle 無關):
根因分析
-
Oracle 端(ORA-00600
[kpp_concatq:2])-
多與 查詢優化器的 Query Transformation/OR 展開 有關。
-
實務上常見誘因:
N'前段'這種 NVARCHAR2 字面量 與資料表VARCHAR2欄位比較,導致 隱式型別轉換;優化器重寫時碰到某些版本 bug 就炸。
-
-
EF Core 端(
CultureInfo常值)-
在
ToListAsync()之前(也就是還在 SQL 端),把ToString("N0", CultureInfo...)、日期ToString(...)之類格式化塞進查詢投影;EF Core 為了避免快取膨脹報錯。
-
三步驟「立即止血」
Step 1. 改用
FromSqlRaw,不要讓 LINQ 自行產 SQL。
Step 2.
參數化 所有常值(例如
"前段"),並
指定正確 OracleDbType 與長度,同時在 SQL 端
CAST
成與欄位一致的型別(VARCHAR2 或
NVARCHAR2)。
Step 3. 在
SELECT 後加
Oracle Hint:/*+ NO_QUERY_TRANSFORMATION */(必要時加 NO_EXPAND)。
安全重構:可直接貼用的程式碼
假設
APS_Z_PLAN.PROCESS_NAME是VARCHAR2(20 CHAR);若為NVARCHAR2,請見後述 FAQ。
若欄位為
NVARCHAR2:把CAST(:proc AS VARCHAR2(20 CHAR))改成CAST(:proc AS NVARCHAR2(20)),並把OracleDbType.Varchar2改為OracleDbType.NVarchar2。
進階避雷:CTE/MATERIALIZE 與 View 包裝
作法 1:CTE +
MATERIALIZE 強制物化
作法 2:建立資料庫 View,Hint 寫死在 View 裡
應用程式就針對 View 查詢,避免每支 SQL 都塞 Hint。
EF Core ToString/CultureInfo
錯誤的正確處理
原則:
所有格式化(千分位、日期字串等)一定放在
ToListAsync() 之後(記憶體端)。
若想同一條鏈上處理,可在查詢後加
.AsEnumerable() 切到客戶端再
Select(...):
版本與長期解法(根治)
-
讓 DBA 查
v$version與opatch lsinventory,確認是否有對應 RU/PSU 可修正kpp_concatq相關已知 Bug。 -
規劃升到 較新且受支援的 19c/23c RU。
-
統一字元型別策略:表欄位是
VARCHAR2就一律用Varchar2參數與CAST;若是NVARCHAR2也全線一致。
效能與穩定性小技巧
-
AsNoTracking():查詢純展示資料時,避免建立追蹤圖。 -
字典快取關聯資料:把
APS_U_LCMREMARK、APS_U_MREMARK轉成Dictionary,避免FirstOrDefaultO(n²)。 -
索引:確保有
(APS_VERSION, PROCESS_NAME, APS_PLAN_LIST)的覆蓋性索引(或至少前兩列)。 -
會話層關掉重寫(可選):
檢查清單(Checklist)
-
用 FromSqlRaw 取代 LINQ 產生的 SQL。
-
所有常值 參數化,指定正確 OracleDbType + 長度。
-
SQL 端對應欄位 CAST 成一致型別。
-
加上
/*+ NO_QUERY_TRANSFORMATION */(必要時NO_EXPAND)。 -
ToListAsync()之後 才做ToString("N0")、日期格式化。 -
remark 轉字典、避免 O(n²)。
-
檢查/更新 Oracle RU/PSU;統一字元型別策略。
FAQ
Q1:我的 PROCESS_NAME 是
NVARCHAR2,怎麼改?
A:把參數型別改為
OracleDbType.NVarchar2,並在
SQL 用
CAST(:proc AS NVARCHAR2(實際長度))。
Q2:不用
FromSqlRaw 可以嗎?
A:理論上可以,但 LINQ 常會產生
N'...'
字面量或不易控制的查詢轉換。為了穩定,建議改
FromSqlRaw。
Q3:只改參數化但不加 Hint 可以嗎?
A:多數情況已足夠,但某些版本仍會在其他重寫路徑踩雷;加上
NO_QUERY_TRANSFORMATION
更保險。
結語
ORA-00600
[kpp_concatq:2]
本質是 Oracle 查詢重寫在特定條件下的內部錯誤。最快速的工程解法就是:FromSqlRaw + 參數/型別對齊 + CAST + Hint。同時,所有資料格式化移到
ToListAsync() 之後,可一併解掉 EF Core 的
CultureInfo 常值錯誤。
把上述模板納入你專案的資料存取慣用法,之後遇到同型問題幾乎都能一次搞定。
留言
張貼留言