Oracle 11g行字段拼接WMSYS.WM_CONCAT問題Not A LOB


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層直接運行仍報錯。

三、問題解決

  1. 去掉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;
  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();
    }
	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;

四、參考文章


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM