how to split string with multiple special characters in oracle - sql

I have a string with 2 special characters as below
String :'PAN~HLASD4564P|VOTER_ID~VDD3455355'
I want output in 2 columns as below :
ID_TYPE VALUE
------- ------
PAN HLASD4564P
VOTER_ID VDD3455355
enter image description here

You can use CONNECT BY and REGEXP_SUBSTR as follows:
SQL> WITH YOUR_DATA AS (
2 SELECT 'PAN~HLASD4564P|VOTER_ID~VDD3455355' AS STR
3 FROM DUAL
4 ) -- Your query starts from here
5 SELECT
6 REGEXP_SUBSTR(NEW_STR, '[^~]+', 1, 1) AS ID,
7 REGEXP_SUBSTR(NEW_STR, '[^~]+', 1, 2) AS VALUE
8 FROM
9 (
10 SELECT REGEXP_SUBSTR(STR, '[^|]+', 1, LEVEL) NEW_STR
11 FROM YOUR_DATA
12 CONNECT BY LEVEL <= 2
13 );
ID VALUE
---------- -------------
PAN HLASD4564P
VOTER_ID VDD3455355
SQL>

Related

How to search for the All the numbers after special character "|" using Oracle

Basically I have few records that in field that has special character usually like following:
id Content
1 1|1232
2 23|12323
3 33|233223
I would like to write a query that select the numbers on the right side of the pipe "|"
so the result should be as follows:
result for query
1 = 1232
2 = 12323
ext...
You can use simple string functions:
SELECT id,
SUBSTR( content, 1, INSTR( content, '|' ) - 1 ) AS before_pipe,
SUBSTR( content, INSTR( content, '|' ) + 1 ) AS after_pipe
FROM table_name
Or, using regular expressions:
SELECT id,
REGEXP_SUBSTR( content, '^\d+' ) AS before_pipe,
REGEXP_SUBSTR( content, '\d+$' ) AS after_pipe
FROM table_name
Which, for the sample data:
CREATE TABLE table_name ( id, content ) AS
SELECT 1, '1|1232' FROM DUAL UNION ALL
SELECT 2, '23|12323' FROM DUAL UNION ALL
SELECT 3, '33|233223' FROM DUAL;
Both output:
ID
BEFORE_PIPE
AFTER_PIPE
1
1
1232
2
23
12323
3
33
233223
db<>fiddle here
Based on sample data, see if any of these two options (substr + instr and regular expressions) help. Sample data in lines #1 - 5, query begins at line#6.
SQL> with test (id, content) as
2 (select 1, '1|1232' from dual union all
3 select 2, '23|12323' from dual union all
4 select 3, '33|233223' from dual
5 )
6 select id,
7 content,
8 --
9 substr(content, instr(content, '|') + 1) result_1,
10 regexp_substr(content, '\d+$') result_2
11 from test;
ID CONTENT RESULT_1 RESULT_2
---------- --------- --------------- ---------------
1 1|1232 1232 1232
2 23|12323 12323 12323
3 33|233223 233223 233223
SQL>

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>

Count characters and their position in a string

I have a string like '###ABC##DE'. I need to find out the position of '#' and then how many '#' after that. For example in this case output should be
position count
======= ======
1 3
7 2
Is it possible to do it in sql statement or do we need plsql block here?
Here's a way of doing it in a SQL statement:
WITH sample_data AS (SELECT '###ABC##DE' str FROM dual UNION ALL
SELECT 'A#B#C#D#E##' str FROM dual UNION ALL
SELECT 'ABCDE' str FROM dual)
SELECT str,
NVL(length(regexp_substr(str, '#+', 1, LEVEL)), 0) num_hashes,
regexp_instr(str, '#+', 1, LEVEL) hash_pos
FROM sample_data
CONNECT BY regexp_substr(str, '#+', 1, LEVEL) IS NOT NULL
AND PRIOR str = str
AND PRIOR sys_guid() IS NOT NULL;
STR NUM_HASHES HASH_POS
----------- ---------- ----------
###ABC##DE 3 1
###ABC##DE 2 7
A#B#C#D#E## 1 2
A#B#C#D#E## 1 4
A#B#C#D#E## 1 6
A#B#C#D#E## 1 8
A#B#C#D#E## 2 10
ABCDE 0 0
What this does is use a hierarchical query (that's the connect by part) to go through the string and search for 1 or more # characters. It will output a row for each group.

Preserve order when converting a delimited string to a column

I want to preserve the record order, which is provided as comma delimited string. The 5th item in by delimited string is a null. I need the 5th row to be null as well.
with test as
(select 'ABC,DEF,GHI,JKL,,MNO' str from dual
)
select rownum, regexp_substr (str, '[^,]+', 1, rownum) split
from test
connect by level <= length (regexp_replace (str, '[^,]+' )) + 1
The current result I'm getting puts this in the 6th position:
1 ABC
2 DEF
3 GHI
4 JKL
5 MNO
6
Order is preserved by your expression, but your regular expression doesn't match nulls correctly, so the 5th item disappears. The 6th row is a NULL because there are no more match after the 5th match.
You could do this instead:
SQL> with test as
2 (select 'ABC,DEF,GHI,JKL,,MNO' str from dual
3 )
4 SELECT rownum,
5 rtrim(regexp_substr(str || ',', '[^,]*,', 1, rownum), ',') split
6 FROM test
7 CONNECT BY LEVEL <= length(regexp_replace(str, '[^,]+')) + 1;
ROWNUM SPLIT
---------- ---------------------------------------------------------------
1 ABC
2 DEF
3 GHI
4 JKL
5
6 MNO
6 rows selected
Or this:
SQL> with test as
2 (select 'ABC,DEF,GHI,JKL,,MNO' str from dual
3 )
4 SELECT rownum,
5 regexp_substr(str, '([^,]*)(,|$)', 1, rownum, 'i', 1) split
6 FROM test
7 CONNECT BY LEVEL <= length(regexp_replace(str, '[^,]+')) + 1;
ROWNUM SPLIT
---------- ------------------------------------------------------------
1 ABC
2 DEF
3 GHI
4 JKL
5
6 MNO
6 rows selected
Try Something like this:
SELECT
STR,
REPLACE ( SUBSTR ( STR,
CASE LEVEL
WHEN 1
THEN
0
ELSE
INSTR ( STR,
'~',
1,
LEVEL
- 1 )
END
+ 1,
1 ),
'~' )
FROM
(SELECT 'A~~C~~E' AS STR FROM DUAL)
CONNECT BY
LEVEL <= LENGTH ( REGEXP_REPLACE ( STR,
'[^~]+' ) )
+ 1;
This one works..
SELECT
ROWNUM,
CAST ( REGEXP_SUBSTR ( STR,
'(.*?)(,|$)',
1,
LEVEL,
NULL,
1 ) AS CHAR ( 12 ) )
OUTPUT
FROM
(SELECT 'ABC,DEF,GHI,JKL,,MNO' AS STR FROM DUAL)
CONNECT BY
LEVEL <= REGEXP_COUNT ( STR,
',' )
+ 1;