Comma separated string to list [duplicate] - sql

This question already has answers here:
split string into several rows
(3 answers)
Closed 6 years ago.
I have a string which as values as below:
Countries = USA, Canada, Italy
I would like to seperate them and display it as list like below
Countries
USA
Canada
Italy
How can I do this using SQL?

There are multiple solutions posted in the Splitting Delimited Strings topic of the Oracle documentation pages.
One is:
Sample Data:
CREATE TABLE table_name ( id, list ) AS
SELECT 1, 'a,b,c,d' FROM DUAL UNION ALL -- Multiple items in the list
SELECT 2, 'e' FROM DUAL UNION ALL -- Single item in the list
SELECT 3, NULL FROM DUAL UNION ALL -- NULL list
SELECT 4, 'f,,g' FROM DUAL; -- NULL item in the list
Query:
SELECT t.id,
v.COLUMN_VALUE AS value,
ROW_NUMBER() OVER ( PARTITION BY id ORDER BY ROWNUM ) AS lvl
FROM table_name t,
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.list, '([^,]*)(,|$)', 1, LEVEL, NULL, 1 )
FROM DUAL
CONNECT BY LEVEL < REGEXP_COUNT( t.list, '[^,]*(,|$)' )
)
AS SYS.ODCIVARCHAR2LIST
)
) v;
Output:
ID ITEM LVL
---------- ------- ----------
1 a 1
1 b 2
1 c 3
1 d 4
2 e 1
3 (NULL) 1
4 f 1
4 (NULL) 2
4 g 3

You can use a regex_substr and a connect by
select regexp_substr( 'USA, Canada, Italy','[^,]+', 1, level) from dual
connect by regexp_substr('USA, Canada, Italy', '[^,]+', 1, level) is not null;

Related

SUBSTR to ADD value in oracle

I have table with column having data in below format in Oracle DB.
COL 1
abc,mno:EMP
xyz:EMP;tyu,opr:PROF
abc,mno:EMP;tyu,opr:PROF
I am trying to convert the data in below format
COL 1
abc:EMP;mno:EMP
xyz:EMP;tyu:PROF;opr:PROF
abc:EMP;mno:EMP;tyu:PROF;opr:PROF
Basically trying to get everything after : and before ; to move it substitute comma with it.
I tried some SUBSTR and LISTAGG but couldn't get anything worth sharing.
Regards.
Here's one option; read comments within code.
SQL> with test (id, col) as
2 -- sample data
3 (select 1, 'abc,mno:EMP' from dual union all
4 select 2, 'xyz:EMP;tyu,opr:PROF' from dual union all
5 select 3, 'abc,mno:EMP;tyu,opr:PROF' from dual
6 ),
7 temp as
8 -- split sample data to rows
9 (select id,
10 column_value cv,
11 regexp_substr(col, '[^;]+', 1, column_value) val
12 from test cross join
13 table(cast(multiset(select level from dual
14 connect by level <= regexp_count(col, ';') + 1
15 ) as sys.odcinumberlist))
16 )
17 -- finally, replace comma with a string that follows a colon sign
18 select id,
19 listagg(replace(val, ',', substr(val, instr(val, ':')) ||';'), ';') within group (order by cv) new_val
20 from temp
21 group by id
22 order by id;
ID NEW_VAL
---------- ----------------------------------------
1 abc:EMP;mno:EMP
2 xyz:EMP;tyu:PROF;opr:PROF
3 abc:EMP;mno:EMP;tyu:PROF;opr:PROF
SQL>
Using the answer of littlefoot, if i were to use cross apply i wouldnt need to cast as multiset...
with test (id, col) as
-- sample data
(select 1, 'abc,mno:EMP' from dual union all
select 2, 'xyz:EMP;tyu,opr:PROF' from dual union all
select 3, 'abc,mno:EMP;tyu,opr:PROF' from dual
),
temp as
-- split sample data to rows
(select id,
column_value cv,
regexp_substr(col, '[^;]+', 1, column_value) val
from test
cross apply (select level as column_value
from dual
connect by level<= regexp_count(col, ';') + 1)
)
-- finally, replace comma with a string that follows a colon sign
select id,
listagg(replace(val, ',', substr(val, instr(val, ':')) ||';'), ';') within group (order by cv) new_val
from temp
group by id
order by id;
You do not need recursive anything, just basic regex: if the pattern is always something,something2:someCode (e.g. you have no colon before the comma), then it would be sufficient.
with test (id, col) as (
select 1, 'abc,mno:EMP' from dual union all
select 2, 'xyz:EMP;tyu,opr:PROF' from dual union all
select 3, 'abc,mno:EMP;tyu,opr:PROF' from dual union all
select 3, 'abc,mno:EMP;tyu,opr:PROF;something:QWE;something2:QWE' from dual
)
select
/*
Grab this groups:
1) Everything before the comma
2) Then everything before the colon
3) And then everything between the colon and a semicolon
Then place group 3 between 1 and 2
*/
trim(trailing ';' from regexp_replace(col || ';', '([^,]+),([^:]+):([^;]+)', '\1:\3;\2:\3')) as res
from test
| RES |
| :------------------------------------------------------------- |
| abc:EMP;mno:EMP |
| xyz:EMP;tyu:PROF;opr:PROF |
| abc:EMP;mno:EMP;tyu:PROF;opr:PROF |
| abc:EMP;mno:EMP;tyu:PROF;opr:PROF;something:QWE;something2:QWE |
db<>fiddle here

Monitoring data with delimiter to create a new rows

I want to create a rows from one row on table that contains a delimiter on some fields as mentioned below on screen shoot
I want a result A separalte rows for the rows that already contains a deleimiter ;
the inputs are data from table 1 and the output is data as mentionned below on table 2 using oracle sql :insert and select query
you can see below the output recommanded:
One method is a recursive CTE:
with cte(id, description, val, before, after, n, cnt) as (
select id, description, val, before, after, 1 as n, regexp_count(description, ';')
from t
union all
select id, description, val, before, after, n + 1, cnt
from cte
where n <= cnt
)
select id,
regexp_substr(description, '[^;]+', 1, n) as description,
regexp_substr(val, '[^;]+', 1, n) as val,
regexp_substr(before, '[^;]+', 1, n) as before,
regexp_substr(after, '[^;]+', 1, n) as after
from cte;
Here is a db<>fiddle.
Alternatively:
SQL> with test (id, description, val, after, before) as
2 -- you already have sample data and don't type this
3 (select 1, 'ARTIC1;ARTIC2;ART11', '15;2;3', '12;6;8', '13;7;12' from dual union all
4 select 2, 'ARTICLE3;ARTICLE4' , '3;5' , '10;23' , '12;25' from dual union all
5 select 3, 'ARTICLE 5' , '6' , '2' , '1.9' from dual
6 )
7 -- query that does the job begins here
8 select id,
9 regexp_substr(description, '[^;]+', 1, column_value) descr,
10 regexp_substr(val , '[^;]+', 1, column_value) val,
11 regexp_substr(after , '[^;]+', 1, column_value) after,
12 regexp_substr(before , '[^;]+', 1, column_value) before
13 from test cross join
14 table(cast(multiset(select level from dual
15 connect by level <= regexp_count(description, ';') + 1
16 ) as sys.odcinumberlist))
17 order by id, descr, val;
ID DESCR VAL AFTER BEFORE
---------- ---------- ---------- ---------- ----------
1 ARTIC1 15 12 13
1 ARTIC2 2 6 7
1 ART11 3 8 12
2 ARTICLE3 3 10 12
2 ARTICLE4 5 23 25
3 ARTICLE 5 6 2 1.9
6 rows selected.
SQL>

Oracle REGEXP_SUBSTR returning NULL as value

I am trying to split data from a column into rows but I am facing this issue here.
When i run this query it splits the data fine but it is also returning NULL as an extra row too.
Here is the value I am trying to split ,162662163,90133140,163268955,169223426,169222899,
WITH CTE AS(
SELECT
RTRIM(LTRIM(PG2.MULTILIST11, ','), ',') ACCESS_BY_GROUPS
FROM AGILE.ITEM I
INNER JOIN AGILE.PAGE_TWO PG2 ON PG2.ID = I.ID
WHERE
ITEM_NUMBER IN --('313-000074',
('313-000090')
)
SELECT DISTINCT
REGEXP_SUBSTR(ACCESS_BY_GROUPS, '[^,]+', 1, column_value) ACCESS_BY_GROUPS
FROM CTE
CROSS JOIN TABLE(CAST(MULTISET(SELECT LEVEL FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT(CTE.ACCESS_BY_GROUPS, ',') + 10
) AS sys.odcinumberlist))
I don't want to get that null value. I cannot apply that check out of the query because it will affect other column values too so I want it to be handled somewhere in the function. I hope someone can help.
It is because of leading and trailing commas.
One option is to begin from position 2 (see line #3) and limit number of values (line #5)
SQL> with test (col) as
2 (select ',162662163,90133140,163268955,169223426,169222899,' from dual)
3 select regexp_substr(col, '[^,]+', 2, level) res
4 from test
5 connect by level <= regexp_count(col, ',') - 1
6 /
RES
-------------------------------------------------
162662163
90133140
163268955
169223426
169222899
SQL>
Another is to remove leading and trailing comma first, and then split the rest into rows:
SQL> with test (col) as
2 (select ',162662163,90133140,163268955,169223426,169222899,' from dual),
3 temp as
4 -- remove leading and trailing comma first
5 (select ltrim(rtrim(col, ','), ',') col
6 from test
7 )
8 select regexp_substr(col, '[^,]+', 1, level) res
9 from temp
10 connect by level <= regexp_count(col, ',') + 1;
RES
------------------------------------------------
162662163
90133140
163268955
169223426
169222899
SQL>
[EDIT - for more than a single row]
If there are more rows involved, code has to be changed. Note that there must be some kind of a unique identifier for every row (ID in my example).
SQL> with test (id, col) as
2 (select 1, ',162662163,90133140,163268955,169223426,169222899,' from dual union all
3 select 2, ',1452761,1452762,' from dual
4 )
5 select id,
6 regexp_substr(col, '[^,]+', 2, column_value) res
7 from test cross join table(cast(multiset(select level from dual
8 connect by level <= regexp_count(col, ',') - 1
9 ) as sys.odcinumberlist))
10 order by id, column_value
11 /
ID RES
---------- -------------------------------------------------
1 162662163
1 90133140
1 163268955
1 169223426
1 169222899
2 1452761
2 1452762
7 rows selected.
SQL>

Splitting records which has | delimiter [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I'm having a two columns say Value column is having records like 'abc|comp|science|raja' and I want to split this records like abc,comp,science,raja and I need to compare it with another column say CHECKER which is having record as science
Value
abc|comp|science|raja
Checkers
science
You don't need to split the string and you don't even need a regular expression; just check whether the checker string (with leading a trailing delimiters) is a sub-string of value (with leading and trailing delimiters):
Oracle Setup:
CREATE TABLE your_table ( value, checker ) as
SELECT 'abc|comp|science|raja', 'science' FROM DUAL UNION ALL
SELECT 'abc|def|ghi', 'xyz' FROM DUAL UNION ALL
SELECT 'abc', 'abc' FROM DUAL UNION ALL
SELECT 'abcdef', 'abc' FROM DUAL
Query:
SELECT *
FROM your_table
WHERE '|' || value || '|' LIKE '%|' || checker || '|%';
Output:
VALUE | CHECKER
:-------------------- | :------
abc|comp|science|raja | science
abc | abc
db<>fiddle here
You can split your pipe delimited string in individual values and represent that as a table. Then you can just join to your Checkers table.
The web is full of examples, here are 2 ways to do it.
The REGEXP way...
WITH test_tab AS
(SELECT 'abc|comp|science|raja' str FROM dual
)
SELECT regexp_substr (str, '[^|]+', 1, rownum) split
FROM test_tab
CONNECT BY LEVEL <= LENGTH (regexp_replace (str, '[^|]+')) + 1;
If you have Application Express in your database, you can use apex_string to do the magic for you:
SELECT
column_value
FROM
TABLE(apex_string.split(
'abc|comp|science|raja',
'|'
));
Here's one option:
SQL> with test (id, value, checkers) as
2 (select 1, 'abc|comp|science|raja', 'science' from dual union all
3 select 2, 'xyz|bla|nothing' , 'zzz' from dual
4 )
5 select t.id,
6 regexp_substr(t.value, '[^\|]+', 1, column_value) val,
7 column_value rn,
8 t.checkers,
9 --
10 case when regexp_substr(t.value, '[^\|]+', 1, column_value) = t.checkers then 'Match'
11 else 'No match'
12 end what
13 from test t cross join table(cast(multiset(select level from dual
14 connect by level <= regexp_count(t.value, '\|') + 1
15 ) as sys.odcinumberlist))
16 order by t.id, rn;
ID VAL RN CHECKER WHAT
---------- --------------------- ---------- ------- --------
1 abc 1 science No match
1 comp 2 science No match
1 science 3 science Match
1 raja 4 science No match
2 xyz 1 zzz No match
2 bla 2 zzz No match
2 nothing 3 zzz No match
7 rows selected.
SQL>
It sounds like you just want to check whether the checker is in the value, in that case you can do this:
with mytable as
( select 'abc|comp|science|raja' value
, 'science' as checker
from dual
union all
select 'science|abc|comp|raja'
, 'science'
from dual
union all
select 'abc|comp|raja|science'
, 'science'
from dual )
select x.value
from mytable x
where regexp_like(value, '(^|\|)' || checker || '($|\|)')
The regex_like is searching inside the value for the checker with either a pipe or the end of the string, so it will match the keyword at the beginning, in the middle or at the end of the string. But it would not match "sciences".
Alternatively if you want to see all the rows and check whether they passed the "check" you could do:
with mytable as
( select 'abc|comp|science|raja' value
, 'science' as checker
from dual
union all
select 'science|abc|comp|raja'
, 'science'
from dual
union all
select 'abc|comp|raja|science'
, 'science'
from dual
union all
select 'abc|comp|raja|sciences'
, 'science'
from dual )
select x.value
, x.checker
, case when regexp_substr(value, '(^|\|)' || checker || '($|\|)') is not null
then 'Y'
end as passed
from mytable x

How to reverse the string 'ab,cd,ef' to 'ef->cd->ab'

when I select the table from Oracle, I want to handle one col'val :
eg:
'ab,cd,ef' to 'ef->cd->ab';
'AB,BC' to 'BC->AB';
'ACNN,BBCCAC' to 'BBCCAC->ACNN';
'BBBDC,DCCX,FFF' to 'FFF->DCCX->BBBDC'
We have two tasks. The first is to tokenize the original strings. This is quite easy with regular expressions (although there are more performant approaches if you are dealing with large volumes). The second task is to re-assemble the tokens in reverse order; we can use the 11gR2 LISTAGG() function for this:
with tokens as (
select distinct col1, regexp_substr(col1, '[^,]+', 1, level) as tkn, level as rn
from t23
connect by level <= regexp_count (col1, '[,]') +1
)
select col1
, listagg(tkn, '->')
within group (order by rn desc) as rev_col1
from tokens
group by col1
/
Here is a SQL Fiddle.
You can do it with a mix of string split and string aggregation.
Using:
REGEXP_SUBSTR : To split the comma delimited string into rows
LISTAGG : To aggregate the values
You can have a look at this article to understand how string split works http://lalitkumarb.wordpress.com/2015/03/04/split-comma-delimited-strings-in-a-table-using-oracle-sql/
SQL> WITH DATA AS(
2 SELECT 1 ID, 'ab,cd,ef' text FROM dual UNION ALL
3 SELECT 2 ID, 'AB,BC' text FROM dual UNION ALL
4 SELECT 3 ID, 'ACNN,BBCCAC' text FROM dual
5 )
6 SELECT ID,
7 listagg(text, ',') WITHIN GROUP (
8 ORDER BY rn DESC) reversed_indices
9 FROM
10 (SELECT t.id,
11 rownum rn,
12 trim(regexp_substr(t.text, '[^,]+', 1, lines.COLUMN_VALUE)) text
13 FROM data t,
14 TABLE (CAST (MULTISET
15 (SELECT LEVEL FROM dual CONNECT BY LEVEL <= regexp_count(t.text, ',')+1
16 ) AS sys.odciNumberList ) ) lines
17 ORDER BY ID
18 )
19 GROUP BY ID
20 /
ID REVERSED_INDICES
---------- ------------------------------
1 ef,cd,ab
2 BC,AB
3 BBCCAC,ACNN
SQL>
Let's say your table looks like:
SQL> SELECT * FROM t;
ID TEXT
---------- ------------------------------
1 ab,cd,ef
2 AB,BC
3 ACNN,BBCCAC
4 word1,word2,word3
5 1,2,3
SQL>
Using the above query:
SQL> SELECT ID,
2 listagg(text, '-->') WITHIN GROUP (
3 ORDER BY rn DESC) reversed_indices
4 FROM
5 (SELECT t.id,
6 rownum rn,
7 trim(regexp_substr(t.text, '[^,]+', 1, lines.COLUMN_VALUE)) text
8 FROM t,
9 TABLE (CAST (MULTISET
10 (SELECT LEVEL FROM dual CONNECT BY LEVEL <= regexp_count(t.text, ',')+1
11 ) AS sys.odciNumberList ) ) lines
12 ORDER BY ID
13 )
14 GROUP BY ID
15 /
ID REVERSED_INDICES
---------- ------------------------------
1 ef-->cd-->ab
2 BC-->AB
3 BBCCAC-->ACNN
4 word3-->word2-->word1
5 3-->2-->1
SQL>