[SQL开发小技巧]一行拆分为多行

测试数据:

CREATE TABLE t (str VARCHAR2(30));
INSERT INTO t VALUES ( 'X,Y,Z' );
INSERT INTO t VALUES ( 'XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG' );

commit;

--1、multiset 8i之后都支持

SQL> CREATE OR REPLACE TYPE number_ntt AS TABLE OF NUMBER;
  2  /
 
Type created
SQL> col value for a20
SQL> 
SQL> with ilv as
  2   (select str || ',' as str,
  3           (length(str) - length(replace(str, ','))) + 1 as no_of_elements
  4      from t)
  5  select a.str, regexp_substr(a.str, '[^,]+', 1, b.column_value) value
  6    from ilv a, table(cast(multiset (select rownum rn
  7                       from dual
  8                     connect by rownum <= a.no_of_elements) as number_ntt)) b;
 
STR                             VALUE
------------------------------- --------------------
X,Y,Z,                          X
X,Y,Z,                          Y
X,Y,Z,                          Z
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, XXX
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, Y
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, ZZ
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, AAAAA
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, B
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, CCC
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, D
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, E
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, F
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG, GGG
 
13 rows selected
 
SQL> 


2、自关联connect by 10g之后
SQL> select str,
  2         regexp_substr(str, '[^,]+', 1, level) value
  3    from t
  4  connect by
  5   str = prior str
  6   and instr(str||',', ',', 1, level) > 0
  7   and prior dbms_random.value is not null;
 
STR                            VALUE
------------------------------ --------------------
X,Y,Z                          X
X,Y,Z                          Y
X,Y,Z                          Z
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG XXX
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG Y
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG ZZ
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG AAAAA
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG B
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG CCC
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG D
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG E
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG F
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG GGG
 
13 rows selected
 
SQL> 

3、自关联,非CONNECT BY方式

SQL> with tmp as (
  2  select t.*,
  3         length(str)-length(regexp_replace(str, ',', ''))+1 len
  4    from t
  5  )
  6  select a.*, regexp_substr(str, '[^,]+', 1, rn) value
  7    from tmp a, (select rownum rn from dual connect by level <= (select max(len) from tmp x)) b
  8   where a.len>=b.rn
  9   order by 1;
 
STR                                   LEN VALUE
------------------------------ ---------- --------------------
X,Y,Z                                   3 Y
X,Y,Z                                   3 Z
X,Y,Z                                   3 X
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 GGG
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 B
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 CCC
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 D
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 E
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 F
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 ZZ
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 Y
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 XXX
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG         10 AAAAA
 
13 rows selected
 
SQL> 

4、Model子句,复杂度有点小高

SQL> col single_element for a15
SQL> 
SQL> with ilv as
  2   (select str as orig_str,
  3           ',' || str || ',' as mod_str,
  4           1 as start_pos,
  5           length(str) as end_pos,
  6           (length(str) - length(replace(str, ','))) + 1 as element_count,
  7           0 as element_no,
  8           rownum as rn
  9      from t)
 10  select orig_str as original_string,
 11         substr(mod_str, start_pos, end_pos - start_pos) as single_element,
 12         element_no,
 13         element_count
 14    from (select *
 15            from ilv
 16           model partition by(rn, orig_str, mod_str)
 17                 dimension by(element_no)
 18                 measures(start_pos, end_pos, element_count)
 19                 rules iterate(2000)
 20                    until(iteration_number + 1 = element_count[0])(
 21                        start_pos[iteration_number + 1] = instr(cv(mod_str), ',', 1, cv(element_no)) + 1,
 22                        end_pos[iteration_number + 1] = instr(cv(mod_str), ',', 1, cv(element_no) + 1)
 23                    )
 24          )
 25   where element_no != 0
 26   order by mod_str, element_no;
 
ORIGINAL_STRING                SINGLE_ELEMENT  ELEMENT_NO ELEMENT_COUNT
------------------------------ --------------- ---------- -------------
X,Y,Z                          X                        1 
X,Y,Z                          Y                        2 
X,Y,Z                          Z                        3 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG XXX                      1 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG Y                        2 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG ZZ                       3 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG AAAAA                    4 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG B                        5 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG CCC                      6 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG D                        7 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG E                        8 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG F                        9 
XXX,Y,ZZ,AAAAA,B,CCC,D,E,F,GGG GGG                     10 
 
13 rows selected
 
SQL> 

其他可以编写自定义函数进行拆分

你可能感兴趣的:([SQL开发小技巧]一行拆分为多行)