oracle11g 拆分字符串的详细技巧

转自:http://m.blog.csdn.net/article/details?id=51946573

<-->功能需求

                有一个比较长的SQL语句,查询出来中间会有类似“abc1,cbd2,db3,db5”这样的行记录,然后想要达到的效果就是将这样的记录按照逗号间隔符拆分出来一条变成4条,这样记录有多条,然后所有有逗号间隔符的都要拆分出来,然后形成新结果集去关联别的表记录。这条长的sql如下:

 

select extractvalue(xmltype(r.approve_content), '/templet/content/nodeId') ids from res_approve_info t

 inner join res_approve_content r on t.res_approve_info_id=r.res_approve_info_id

 where

  t.auth_type_cd='JHGL_KFJH_10' and t.status_cd='2' and t.created_date >to_date('2016-01-01','yyyy-mm-dd')                                                                                                        

and extractvalue(xmltype(r.approve_content), '/templet/content/isOnTimeOrDelay')='2'

 

 

1、思路分析

核心在于拆分字符串,拆分字符串sql参考先拆分然后再整合成一个临时表,拆分表达式已经想好了大概有如下2种方法:

 

(1)      正则表达式的方式

(2)      存储函数的方式

 

 

2、正则表达式的实现方式

必须是oracle 10g+的版本才支持,以逗号间隔:

SELECTREGEXP_SUBSTR ('abc1,cbd2,db3,db5', '[^,]+', 1,rownum)

  FROM DUAL

  CONNECTBYROWNUM <=

  LENGTH ('abc1,cbd2,db3,db5') - LENGTH (REPLACE ('abc1,cbd2,db3,db5', ',', ''))+1;

 

执行如下:

SQL>

SQL> SELECT REGEXP_SUBSTR ('abc1,cbd2,db3,db5', '[^,]+', 1,rownum)

  2    FROM DUAL

  3    CONNECTBYROWNUM <=

  4    LENGTH ('abc1,cbd2,db3,db5') - LENGTH (REPLACE ('abc1,cbd2,db3,db5', ',', ''))+1;                                                                                                                                                 

REGEXP_SUBSTR('ABC1,CBD2,DB3,D

----------------------------------

abc1

cbd2

db3

db5

 

SQL>

 

 

3、以类型和函数的方式实现

(1)建立TYPE类型

CREATEORREPLACETYPE str_split ISTABLEOFVARCHAR2 (4000)

 

 

(2)建立FUNCTION存储函数

CREATEORREPLACEFUNCTION fun_splitstr(p_string INVARCHAR2, p_delimiter INVARCHAR2)                                                                                                                                 

    RETURN str_split

    PIPELINED

AS

    v_length   NUMBER := LENGTH(p_string);

    v_start    NUMBER := 1;

    v_index    NUMBER;

BEGIN

    WHILE(v_start <= v_length)

    LOOP

        v_index := INSTR(p_string, p_delimiter, v_start);

 

        IF v_index = 0

        THEN

            PIPEROW(SUBSTR(p_string, v_start));

            v_start := v_length + 1;

        ELSE

            PIPEROW(SUBSTR(p_string, v_start, v_index - v_start));

            v_start := v_index + 1;

        ENDIF;

    ENDLOOP;

 

    RETURN;

END fun_splitstr;

 

(3)      开始验证使用函数

SQL> select * fromtable(select fun_splitstr('abc1,cbd2,db3,db5',',') ids from dual)t1;                                                                                                                                                               

COLUMN_VALUE

--------------------------------------------------------------------------------

abc1

cbd2

db3

db5

 

SQL>

 

 

4、效率比较高的办法

(1),在java代码(或者存储过程)里面循环遍历如下原始结果集,

 

(2),通过拆分函数,按行循环来拆,把每一个拆出来的结果都插入到一个临时表或者临时集合t3里面,使用select * from table(fun_splitstr('aaa,bbb,ccc',','));来实现。

 

(3),最后你用这个临时集合去关联你需要关联的表就可以了

select t4.* from t3 left join plan6_node t4where t4.id=t3.id

 

 

5、效率比较低的办法(with临时表)

采用with临时表的办法来实现,sql如下,不过效率比较低:

with temp0 as (selectLEVEL lv from dual CONNECTBYLEVEL <= 100) 

      selectid,substr(t.vals,instr(t.vals, ',', 1, tv.lv) + 1, 

                         instr(t.vals, ',', 1, tv.lv + 1) -( 

                         instr(t.vals, ',', 1, tv.lv) + 1) 

                  ) ASname 

      from (selectid,',' || name || ','AS vals, 

                 length(name || ',') - nvl(length(REPLACE(name, ',')), 0) AS cnt 

            from (select1asid,'abc1,cbd2,db3,db5'asname  from dual union  allselect2, 'zhangsan1,lisi2,wanger3'from dual)                                                                                               

           ) t join temp0 tv 

                      on  tv.lv <= t.cnt  orderby1;

 

 

执行过程如下:

SQL> with temp0 as (selectLEVEL lv from dual CONNECTBYLEVEL <= 100)

  2        selectid,substr(t.vals,instr(t.vals, ',', 1, tv.lv) + 1,

  3                           instr(t.vals, ',', 1, tv.lv + 1) -(

  4                           instr(t.vals, ',', 1, tv.lv) + 1)

  5                    ) ASname

  6        from (selectid,',' || name || ','AS vals,

  7                   length(name || ',') - nvl(length(REPLACE(name, ',')), 0) AS cnt

  8              from (select1asid,'abc1,cbd2,db3,db5'asname  from dual union  allselect2, 'zhangsan1,lisi2,wanger3'from dual)                                                                                        

  9             ) t join temp0 tv

 10                        on  tv.lv <= t.cnt  orderby1;

        IDNAME

---------- --------------------------------------------------

         1 abc1

         1 db3

         1 cbd2

         1 db5

         2 wanger3

         2 zhangsan1

         2 lisi2

7rows selected

 

SQL>

 

 

这里效率比较低的原因是:select1asid,'abc1,cbd2,db3,db5'asname  from dual union  allselect2, 'zhangsan1,lisi2,wanger3'from dual,这里临时表,以为需要大量不停的循环遍历它,如果表数据量大或者获取比较复杂的话,这里就会卡死的。

 

 

临时解决办法是,createtable z_temp2 asselect1asid,'abc1,cbd2,db3,db5'asname  from dual union allselect2, 'zhangsan1,lisi2,wanger3'from dual;然后连接这个temp1表进行操作,那么总体sql如下:

createtable z_temp2 asselect1asid,'abc1,cbd2,db3,db5'asname  from dual union  allselect2, 'zhangsan1,lisi2,wanger3'from dual;                                                                          

with temp0 as (selectLEVEL lv from dual CONNECTBYLEVEL <= 100) 

      selectid,substr(t.vals,instr(t.vals, ',', 1, tv.lv) + 1, 

                         instr(t.vals, ',', 1, tv.lv + 1) -( 

                         instr(t.vals, ',', 1, tv.lv) + 1) 

                  ) ASname 

      from (selectid,',' || name || ','AS vals, 

                 length(name || ',') - nvl(length(REPLACE(name, ',')), 0) AS cnt 

            from z_temp2

           ) t join temp0 tv 

                      on  tv.lv <= t.cnt  orderby1;

 

 

这样用临时表的缺陷就是,不能一条sql搞定,需要分2个阶段来执行,而且每次都需要清空临时表z_temp2,这点比较麻烦。

 

参考文档:http://www.anbob.com/archives/221.html

posted @ 2016-11-18 10:30  李清欣  阅读(306)  评论(0编辑  收藏  举报