Sam
Sam

Reputation: 11

Split column into multiple rows in ORACLE based on static length of substring

I have seen multiple topics here for "Split column into multiple rows" but they all are based on some delimiter.

I want to split the column based on length in oracle.

Suppose i have a table

codes                     | product
--------------------------+--------
C111C222C333C444C555..... |  A   

codes are type VARCHAR2(800) and product is VARCHAR2(1).

Here in codes field we have many codes (maximum 200) which belongs to product A. and length of each code is 4 ( so C111, C222, C333 are different codes)

I want output of my select query like this-

code          |  product
---------------+-------
C111           |    A
C222           |    A
C333           |    A
C444           |    A
C555           |    A
...

and so on.

please help me with this. Thanks in advance.

Upvotes: 1

Views: 3229

Answers (4)

Gary_W
Gary_W

Reputation: 10360

Here's yet another variation using regexp_substr() along with CONNECT BY to "loop" through the string by 4 character substrings:

SQL> with tbl(codes, product) as (
     select 'C111C222C333C444C555', 'A' from dual union all
     select 'D111D222D333', 'B' from dual
   )
   select regexp_substr(codes, '(.{4})', 1, level, null, 1) code, product
   from tbl
   connect by level <= (length(codes)/4)
     and prior codes = codes
     and prior sys_guid() is not null;

CODE                 P
-------------------- -
C111                 A
C222                 A
C333                 A
C444                 A
C555                 A
D111                 B
D222                 B
D333                 B

8 rows selected.

SQL>

Upvotes: 1

Alex D
Alex D

Reputation: 43

Yet another a little bit different option of using recursive SQL to do this. (To make it more concise I didn't add an example of test data. It could be taken from @Littlefoot or @Peter answers)

select code, product
  from (
        select distinct
               substr(codes, (level - 1) * 4 + 1, 4) as code,
               level as l,
               product
          from YourTable
       connect by substr(codes, (level - 1) * 4 + 1, 4) is not null
       )
order by product, l 

P.S. @Thorsten Kettner made a fair point about considering to restructure your tables. That would be the right thing to do for sake of easier maintenance of your database in future

Upvotes: 0

Littlefoot
Littlefoot

Reputation: 142705

One option might be this:

SQL> with test (codes, product) as
  2    (select 'C111C222C333C444C555', 'A' from dual union all
  3     select 'D555D666D777', 'B' from dual
  4    )
  5  select substr(codes, 4 * (column_value - 1) + 1, 4) code, product
  6  from test,
  7       table(cast(multiset(select level from dual
  8                           connect by level <= length(codes) / 4
  9                          ) as sys.odcinumberlist))
 10  order by 1;

CODE P
---- -
C111 A
C222 A
C333 A
C444 A
C555 A
D555 B
D666 B
D777 B

8 rows selected.

SQL>

Upvotes: 0

Peter
Peter

Reputation: 1196

Here is how I would do it. Let me know if you need more input / better explanations:

select substr(tt.codes,(((t.l-1)*4)+1),4) code,tt.product from tst_tab tt
    join (select level l from dual connect by level <= (select max(length(codes)/4) from tst_tab)) t 
    on t.l <= length(tt.codes)/4 
order by tt.product,t.l;

Some explanantions:

-- this part gives the numbers from 1 ... maximum number of codes in codes column
select level l from dual connect by level <= (select max(length(codes)/4) from tst_tab);
-- here is the query without the code extraction, it is just the numbers 1... numbers of codes for the product
select t.l,tt.product from tst_tab tt
    join (select level l from dual connect by level <= (select max(length(codes)/4) from tst_tab)) t 
    on t.l <= length(tt.codes)/4
order by tt.product,t.l;  
-- and then the substr just extracts the right code: 
substr(tt.codes,(((t.l-1)*4)+1),4)

Set up of my test data:

create table tst_tab (codes VARCHAR2(800),product VARCHAR2(1));
insert into tst_tab values ('C111C222C333C444C555','A');
insert into tst_tab values ('C111C222C333C444C555D666','B');
insert into tst_tab values ('C111','C');
commit;

Upvotes: 1

Related Questions