昨天在測試的時候發現,開發人員寫的一段程式放在開發庫中是好的,但是放在測試庫中就會有問題。開發人員一直找不到問題的原因在哪裡。於是就花了點時間協助開發人員來找問題的根本原因。
通過一些技術手段,定位到問題是由一個SQL語句引起的。
SQL語句如下:
WITH PXQ_TASK_TEMP AS (SELECT A.DISTRIBUTE_DATE, A.APP_NO, A.TASK_ID, A.TASK_NO, A.ACTUAL_DISTRIBUTE_DATE, A.ACTUAL_RETURN_DATE, A.RELA_ID RELAID, A.VEHICLE_SERIAL, A.STATUS_CODE, A.MONTH, A.MADE_DPET_NO, A.DIST_TYPE_CODE, A.TASK_TYPE, A.RETURN_DATE, B.SOURCE_NODE_ID, B.RCV_NODE_NAME, B.RCV_NODE_ID, B.RCV_ID, C.DET_ID, C.EQUIP_CATEG, C.TASK_NUM, D.RELA_ID FROM D_DISTRIBUTE_TASK A, D_RCV_TASK B, D_DIST_TASK_DET C, ECMS.D_VEHICLE_TASK_RELA D WHERE A.TASK_ID = B.TASK_ID(+) AND B.RCV_ID = C.RCV_ID(+) AND A.TASK_ID = D.TASK_ID(+)),PXQ_TASK_CATEG AS (SELECT DISTINCT TASK_ID, RCV_ID, DET_ID, EQUIP_CATEG, TASK_NUM FROM PXQ_TASK_TEMP),PXQ_TASK_SUM AS (SELECT V.NAME || ':' || SUM(TASK_NUM) TASK_SUM, MAX(PXQ.TASK_ID) TASK_ID FROM V_SP_CODE V, PXQ_TASK_CATEG PXQ WHERE CODE_TYPE LIKE 'equipCateg' AND VALUE = EQUIP_CATEG GROUP BY PXQ.TASK_ID, V.NAME)SELECT DISTINCT AA.DISTRIBUTE_DATE PLAN_DATE, AA.APP_NO, AA.MONTH, AA.TASK_ID, AA.TASK_NO, AA.ACTUAL_DISTRIBUTE_DATE, AA.ACTUAL_RETURN_DATE, AA.RELAID, AA.VEHICLE_SERIAL EV_NO, AA.STATUS_CODE, AA.DIST_TYPE_CODE, AA.TASK_TYPE, AA.RETURN_DATE, (SELECT REPLACE(WMSYS.WM_CONCAT(NODE_NAME), ',', ';') FROM ECMS.D_DISTRIBUTE_NODE WHERE NODE_ID IN (SELECT DISTINCT SOURCE_NODE_ID FROM PXQ_TASK_TEMP WHERE TASK_ID = AA.TASK_ID)) SOURCE_NODE, (SELECT REPLACE(WMSYS.WM_CONCAT(NODE_NAME), ',', ';') FROM ECMS.D_DISTRIBUTE_NODE WHERE NODE_ID IN (SELECT DISTINCT RCV_NODE_ID FROM PXQ_TASK_TEMP WHERE TASK_ID = AA.TASK_ID)) RCV_NODE, (SELECT REPLACE(WMSYS.WM_CONCAT(TASK_SUM), ',', '/') FROM PXQ_TASK_SUM WHERE TASK_ID = AA.TASK_ID) PLAN_NUM, '' EV_NUM, '' SETTED_EV FROM PXQ_TASK_TEMP AA WHERE 1 = 1;
把這個SQL語句放在開發庫可以正常執行,但是放在測試庫就報錯了。報錯資訊為:ORA-00932:資料類型不一致:應為 -,但卻獲得CLOB。當時想到的就是兩個庫中可能有些表的資料庫資料結構不一致(如:欄位的資料類型),經比較這個SQL語句中涉及到的表在兩個環境中的資料結構完全一致。這時想到的就是可能是某個函數導致了結果出現了LOB類型。經診斷是由oracle函數WMSYS.WM_CONCAT引起的。那麼為什麼會引起這樣的問題呢?兩套環境同樣是使用的oracle,程式也完全一致。首先想到的是可能oracle的WMSYS.WM_CONCAT函數存在版本差異。
下面就進行了WMSYS.WM_CONCAT函數在oracle版本差異的驗證。在開發庫中執行SELECT * FROM v$version;顯示結果為:Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi;再在測試庫中執行SELECT * FROM v$version;顯示結果為:Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi。說明oracle版本差異的猜測是對的。
接下來看WMSYS.WM_CONCAT函數在不同oracle版本中到底存在什麼差異。
編寫測試SQL:
SELECT REPLACE(WMSYS.WM_CONCAT(NODE_NAME), ',', ';') FROM ECMS.D_DISTRIBUTE_NODE A WHERE NODE_ID IN (SELECT DISTINCT SOURCE_NODE_ID FROM D_RCV_TASK B);
該SQL在開發庫中(即oracle版本為:Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi)執行結果為:
該SQL在測試庫中(即oracle版本為:Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi)執行結果為:
oracle的WMSYS.WM_CONCAT函數在Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi版本中的結果是一個字元型的資料,而在Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi版本中是一個CLOB類型。而我們知道在SQL語句中若查詢了LOB欄位是不能使用distinct,union,和group by等關鍵字的。這樣就完全解釋了那個SQL語句會在測試庫中報錯的原因了。
最後將上面的SQL語句做了相關最佳化,且在不同oracle版本中通用。SQL語句修改後如下:
SELECT A.DISTRIBUTE_DATE PLAN_DATE, A.APP_NO, A.MONTH, A.TASK_ID, A.TASK_NO, A.ACTUAL_DISTRIBUTE_DATE, A.ACTUAL_RETURN_DATE, A.RELA_ID, A.VEHICLE_SERIAL EV_NO, A.STATUS_CODE, A.DIST_TYPE_CODE, A.TASK_TYPE, A.RETURN_DATE, (SELECT REPLACE(WMSYS.WM_CONCAT(NODE_NAME), ',', ';') FROM ECMS.D_DISTRIBUTE_NODE WHERE NODE_ID IN (SELECT DISTINCT SOURCE_NODE_ID FROM D_RCV_TASK WHERE TASK_ID = A.TASK_ID)) SOURCE_NODE, (SELECT REPLACE(WMSYS.WM_CONCAT(NODE_NAME), ',', ';') FROM ECMS.D_DISTRIBUTE_NODE WHERE NODE_ID IN (SELECT DISTINCT RCV_NODE_ID FROM D_RCV_TASK WHERE TASK_ID = A.TASK_ID)) RCV_NODE, (SELECT REPLACE(WMSYS.WM_CONCAT(V.NAME || ':' || SUM(C.TASK_NUM)), ',', '/') FROM V_SP_CODE V, D_DIST_TASK_DET C WHERE C.TASK_ID = A.TASK_ID AND V.CODE_TYPE LIKE 'equipCateg' AND V.VALUE = C.EQUIP_CATEG GROUP BY V.NAME) PLAN_NUM, '' EV_NUM, '' SETTED_EV FROM D_DISTRIBUTE_TASK A, ECMS.D_VEHICLE_TASK_RELA B WHERE A.TASK_ID = B.TASK_ID(+) GROUP BY A.DISTRIBUTE_DATE, A.APP_NO, A.MONTH, A.TASK_ID, A.TASK_NO, A.ACTUAL_DISTRIBUTE_DATE, A.ACTUAL_RETURN_DATE, A.RELA_ID, A.VEHICLE_SERIAL, A.STATUS_CODE, A.DIST_TYPE_CODE, A.TASK_TYPE, A.RETURN_DATE;
通過這次問題的排查,我們知道在寫SQL語句的時候,若使用了WMSYS.WM_CONCAT函數,應盡量不要對查詢結果進行distinct,union,和group by操作,避免oracle版本的差異化。