Oracle 11g行欄位拼接WMSYS.WM_CONCAT問題Not A LOB 一、問題出現 項目中的某個查詢需要將表中某個欄位不重覆地拼接起來,百度得到該函數 ,以及 函數,前者只能使用逗號' '連接,後者可以定製連接符。 但由於listagg不能直接在參數中使用 去重,因此採用 函數。 SQ ...
Oracle 11g行欄位拼接WMSYS.WM_CONCAT問題Not A LOB
一、問題出現
項目中的某個查詢需要將表中某個欄位不重覆地拼接起來,百度得到該函數WMSYS.WM_CONCAT(欄位)
,以及listagg(欄位,連接符)
函數,前者只能使用逗號',
'連接,後者可以定製連接符。
但由於listagg不能直接在參數中使用distinct
去重,因此採用WM_CONCAT
函數。
SQL格式如下:
select t.id, t.pjname
from (select A.id as id, count(distinct B.name) as countname,
to_char(wmsys.wm_concat(distinct to_char(B.name))) as pjname
from A left join B on A.id = B.id
where 1 = 1
group by A.id) t
where t.countname > 1;
這段SQL的作用是,以A表的id為組,不重覆的拼接B表的name,並統計name去重後的個數,最後返回name去重後仍多於1個的id和拼接name。
開發時這段SQL是正常的,然而,這段SQL在測試庫上卻會報錯ORA-22922: 不存在的 LOB 值
。
二、原因分析
經網上查資料,發現問題出在WMSYS.WM_CONCAT
函數在Oracle不同版本中的返回值類型不同。
該項目開發使用的是Oracle 11.2.0.1.0,而測試與現場使用的均為Oracle 11.2.0.4.0,項目開始時的疏忽導致開發與測試的不一致。
將拼接函數外的to_char去掉後,SQL不會報錯,但對象不是String類型(可能是java.sql.Clob
類型),無法直接toString獲得。
同時,在PLSQL Developer 9.0中直接運行SQL時,該拼接結果直接顯示為<CLOB>
,可在select結果中使用to_char()
函數,而該函數在項目dal層直接運行仍報錯。
三、問題解決
- 去掉WM_CONCAT函數外的to_char()
select t.id, t.pjname
from (select A.id as id, count(distinct B.name) as countname,
wmsys.wm_concat(distinct to_char(B.name)) as pjname
from A left join B on A.id = B.id
where 1 = 1
group by A.id) t
where t.countname > 1;
- 將LOB類型對象轉換為String類型,有兩種方法:在SQL中使用Oracle函數,或者在後端dal層轉換,參考網上的文章,我選擇後者,因為完整的SQL要實現的功能本身比較複雜,要儘量簡化在資料庫中的操作。
- 獲取結果集中的欄位並判斷
String array1 = "";
try {
array = array[1].getClass().toString().equals("class java.lang.String") ? array[1].toString() : ClobToString((Clob) array[1]);
} catch (SQLException e) {
array14 = array[1].toString();
} catch (IOException e) {
e.printStackTrace();
}
- 轉換CLOB為String對象(參考Oracle中將Clob欄位轉換成字元串)
public String ClobToString(Clob clob) throws SQLException, IOException {
String reString = "";
Reader is = clob.getCharacterStream();
BufferedReader br = new BufferedReader(is);
String s = br.readLine();
StringBuffer sb = new StringBuffer();
while (s != null) {
sb.append(s);
s = br.readLine();
}
reString = sb.toString();
if(br!=null){
br.close();
}
if(is!=null){
is.close();
}
return reString;
}
問題解決。還是得看看listagg方法的用法,畢竟官方相容性強些,但覺得listagg
不如WM_CONCAT
簡單易用。
三、補充
listagg(列名,'分隔符')
除了要多嵌套一層子查詢,其實也挺方便的,它是從11g起才出現的聚合函數,要實現去重和統計,需要使用distinct
的多列去重。
select A.id, t.countname, t.pjname
from A
left join (SELECT id,count(name) as countname,
LISTAGG(to_char(name), ',') WITHIN GROUP(ORDER BY name) AS pjname
FROM (select distinct B.id as id, B.name as name
from B
left join C
on B.name = C.name
where C.gender = 'female')
where 1 = 1
group by id) t
on A.id = t.id
where countname is null or countname <= 1;