Reputation: 444
I'm trying to split a comma separated list in a column to different rows. For example:
with testdata(vala,valb,valc) as(
select
'1,2,3,4' as vala,
'5,6,7,8' as valb,
'9,10,11' as valc
from dual)
Using the table above I'm trying to get the values in such a form that vala,valb and valc are the columns, the different comma separated values are the rows. As shown in the image below:
I already had a look into https://lalitkumarb.wordpress.com/2014/12/02/split-comma-delimited-string-into-rows-in-oracle/ and https://blogs.oracle.com/aramamoo/how-to-split-comma-separated-string-and-pass-to-in-clause-of-select-statement but I seem to be unable to apply the methods described as I have more columns...
The reason why I need to do this transformation is to use these values in a IN expression further down the query.
Upvotes: 0
Views: 309
Reputation: 168623
A solution that only uses simple string functions (and not slow regular expressions), doesn't need any joins and can also handle multiple input rows:
Oracle Setup:
CREATE TABLE testdata (vala, valb, valc) AS
SELECT '1,2,3,4','5,6,7,8', '9,10,11' FROM DUAL UNION ALL
SELECT '12', '13,14,15,16', '' FROM DUAL;
Query:
WITH data ( vala, valb, valc, starta, startb, startc, enda, endb, endc, rn, idx ) AS (
SELECT vala,
valb,
valc,
1,
1,
1,
INSTR( vala, ',', 1 ),
INSTR( valb, ',', 1 ),
INSTR( valc, ',', 1 ),
ROWNUM,
1
FROM testdata
UNION ALL
SELECT vala,
valb,
valc,
CASE WHEN enda = 0 THEN 0 ELSE enda + 1 END,
CASE WHEN endb = 0 THEN 0 ELSE endb + 1 END,
CASE WHEN endc = 0 THEN 0 ELSE endc + 1 END,
CASE WHEN enda = 0 THEN 0 ELSE INSTR( vala, ',', enda + 1 ) END,
CASE WHEN endb = 0 THEN 0 ELSE INSTR( valb, ',', endb + 1 ) END,
CASE WHEN endc = 0 THEN 0 ELSE INSTR( valc, ',', endc + 1 ) END,
rn,
idx + 1
FROM data
WHERE enda > 0
OR endb > 0
OR endc > 0
)
SELECT CASE
WHEN starta = 0 THEN NULL
WHEN enda = 0 THEN SUBSTR( vala, starta )
ELSE SUBSTR( vala, starta, enda - starta )
END AS vala,
CASE
WHEN startb = 0 THEN NULL
WHEN endb = 0 THEN SUBSTR( valb, startb )
ELSE SUBSTR( valb, startb, endb - startb )
END AS valb,
CASE
WHEN startc = 0 THEN NULL
WHEN endc = 0 THEN SUBSTR( valc, startc )
ELSE SUBSTR( valc, startc, endc - startc )
END AS valc
FROM data
ORDER BY rn, idx;
Output:
VALA | VALB | VALC :--- | :--- | :--- 1 | 5 | 9 2 | 6 | 10 3 | 7 | 11 4 | 8 | null 12 | 13 | null null | 14 | null null | 15 | null null | 16 | null
db<>fiddle here
Upvotes: 0
Reputation: 12843
This should work, regardless of the column "height".
with testdata(vala, valb, valc) as(
select '1,2,3,4' as vala
,'5,6,7,8' as valb
,'9,10,11' as valc
from dual
)
select regexp_substr(vala,'[^,]+', 1, level) as vala -- Pick the
,regexp_substr(valb,'[^,]+', 1, level) as valb -- value at this
,regexp_substr(valc,'[^,]+', 1, level) as valc -- position or null
from testdata
connect by regexp_substr(vala,'[^,]+', 1, level) is not null -- Generate one row if
or regexp_substr(valb,'[^,]+', 1, level) is not null -- any of the positions
or regexp_substr(valc,'[^,]+', 1, level) is not null -- would have a value
Upvotes: 2
Reputation: 7394
try this:
with testdata(vala,valb,valc)
as(
select
'1,2,3,4' as vala,
'5,6,7,8' as valb,
'9,10,11' as valc
from dual)
,
main as (
select rownum rn,
regexp_substr(vala,'[^,]+', 1, level) data from
testdata
connect by regexp_substr(vala,'[^,]+', 1, level) is not null
),
sub as (
select rownum rn,
regexp_substr(valb,'[^,]+', 1, level)data from
testdata
connect by regexp_substr(valb,'[^,]+', 1, level) is not null
),
sub2 as (
select rownum rn,
regexp_substr(valc,'[^,]+', 1, level)data from
testdata
connect by regexp_substr(valc,'[^,]+', 1, level) is not null
)
select
main.data,
sub.data,
sub2.data
from
main
full outer join
sub on main.rn=sub.rn
full outer join
sub2 on main.rn=sub2.rn
Upvotes: 1