Oracle Regexp_substr to return a value from a yaml string - oracle

I am trying to parse the yaml string to a column in SQL.
I have a yaml string in one of the columns like
country_cd:GB
postal_town_city:London
zip:CT11 A1Cr
I have to write a query to pass the key and get a value. When I pass 'country_cd', I want to receive 'GB'.
I have tried the below script but it is returning 'country_cd:G'. What am I doing incorrectly ?
select trim(regexp_substr('---
country_cd:GB
postal_town_city:London
zip:CT11 A1Cr', '\n?'||'postal_town_city'||'*[:=] *(.+?) *\n?', 1, 1, 'i')) from dual ;

You can use REGEXP_SUBSTR with the m match parameter to parse the string as separate lines:
SELECT REGEXP_SUBSTR(yaml, '^country_cd:(.*)$', 1, 1, 'm', 1) AS value
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (yaml) AS
SELECT 'country_cd:GB
postal_town_city:London
zip:CT11 A1Cr' FROM DUAL;
Outputs:
VALUE
GB
or, you can split the string using simple string functions (which are often quicker than using regular expressions) and then filter for the correct key:
WITH lines (yaml, line_start, separator_pos, line_end) AS (
SELECT yaml,
1,
INSTR(yaml, ':', 1),
INSTR(yaml, CHR(10), 1)
FROM table_name
UNION ALL
SELECT yaml,
line_end + 1,
INSTR(yaml, ':', line_end + 1),
INSTR(yaml, CHR(10), line_end + 1)
FROM lines
WHERE line_end > 0
)
SELECT SUBSTR(yaml, line_start, separator_pos - line_start) AS key,
CASE line_end
WHEN 0
THEN SUBSTR(yaml, separator_pos + 1)
ELSE SUBSTR(yaml, separator_pos + 1, line_end - separator_pos)
END AS value
FROM lines
WHERE SUBSTR(yaml, line_start, separator_pos - line_start) = 'country_cd'
Which outputs:
KEY
VALUE
country_cd
GB
db<>fiddle here

Using REGEXP_SUBSTR with a capture group, we can try:
SELECT yaml,
REGEXP_SUBSTR(yaml_col, '(^|\s)country_cd:(\S+)', 1, 1, NULL, 2) AS country_cd
FROM yourTable;
Here is regex demo.

Related

How to extract a value inside column in certain pattern in oracle

I want to extract data in the format (465797,461396) from (',7809628#465797,7809628#461396,') data. How can I do that?
[DBFiddle][1]
select
ltrim(
regexp_replace(
',7809628#465797,7809628#461396,'
,'[^#]*#(\d+),'
,',\1')
,','
) new_str
from dual;
Results:
NEW_STR
-------------
465797,461396
Regexp '[^#]*#(\d+),':
[^#]* - any number of any symbols except '#'
(\d+) - digits, one or more digits, () - marks it as a group
and comma after that.
So it finds (any symbols except #, then #, then one or more digits, then comma) and replaces all found substrings to comma and found "one or more digits"(the only group in the expression). Then ltrim removes extra comma in the beggining.
[1]: https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=f66505cd2746a1387e41ec91d6c7df3a
You can use simple string functions (which may be more to type than with a regular expression but is likely to execute quicker):
SELECT SUBSTR(value, hash1 + 1, comma2 - hash1 - 1)
|| ',' ||
SUBSTR(value, hash2 + 1, comma3 - hash2 - 1) AS parsed_value
FROM (
SELECT value,
INSTR(value, '#', 1, 1) AS hash1,
INSTR(value, '#', 1, 2) AS hash2,
INSTR(value, ',', 1, 2) AS comma2,
INSTR(value, ',', 1, 3) AS comma3
FROM table_name
)
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT ',7809628#465797,7809628#461396,' FROM DUAL;
Outputs:
PARSED_VALUE
465797,461396
db<>fiddle here

Regexp_substr find string not matching a group of characters

I have a string like mystr = 'value1~|~value2~|~ ... valuen". I need it as one column separated on rows like this:
value1
value2
...
valuen
I'm trying this
select regexp_substr(mystr, '[^(~\|~)]', 1 , lvl) from dual, (select level as lvl from dual connect by level <= 5);
The problem is that ~|~ is not treated as a group, if I add ~ to anywhere in the string it gets separated; also () are treated as separators.
Any help is highly appreciated! Thanks! ~|~
Quick and dirty solution:
with t as (
select rtrim(regexp_substr('value1~|~value2~|~value3~|~value4', '(.+?)($|~\|~)', 1,level,''),'~|~')value from dual connect by level<10
) select * from t where value is not null;
[] signifies a single character match and [^] signifies a single character that does not match any of the contained characters.
So [^(~\|~)] will match any one character that is not ( or ~ or \ or | or ~ (again) or ).
What you want is a match that is terminated by your separator:
SELECT REGEXP_SUBSTR(
mystr,
'(.*?)(~\|~)',
1,
LEVEL,
NULL,
1
)
FROM DUAL
CONNECT BY LEVEL < REGEXP_COUNT( mystr, '(.*?)(~\|~)' );
(or if you cannot have zero-width matches, you can use the regular expression '(.+?)(~\|~)' and <= in the CONNECT BY clause.)
This will parse the delimited list and the format of the regex will handle NULL list elements should they occur as shown in the example.
SQL> with tbl(str) as (
select 'value1~|~value2~|~~|~value4' from dual
)
select regexp_substr(str, '(.*?)(~\|~|$)', 1, level, NULL, 1) parsed
from tbl
connect by level <= regexp_count(str, '~\|~')+1;
PARSED
--------------------------------
value1
value2
value4
SQL>

How to add new line separator into data for oracle

I'm working on displaying data from oracle.
is there a way to make the following data inside the table:
example :
'1.somedata, 2.somedata, 3.somedata, 4.somedata, 5.somedata'
to display like:
example:
'1. somedata
2. somedata
3. somedata
4. somedata
5. somedata'
on the interface?
do i add new line separator directly into the data?
or do i separator them into new line when i query it?
or is there any other simple way?
Thanks.
There are so many ways to do this, here is one if you are selecting from a column:
SELECT REPLACE ('1.somedata, 2.somedata, 3.somedata, 4.somedata, 5.somedata', ',', CHR (13) || CHR (10)) AS split
FROM DUAL;
1.somedata
2.somedata
3.somedata
4.somedata
5.somedata
I personally would use the listagg function and use '' as the delimiter.
SELECT LISTAGG(last_name, ' ')
WITHIN GROUP (ORDER BY hire_date, last_name) "Emp_list",
MIN(hire_date) "Earliest"
FROM employees
WHERE department_id = 30;
Remember that Apex is generating a web page, which means the end result is HTML. Apex, however, will also sometimes escape special HTML characters for you, like < and &. Since you're viewing a table, I assume the source of your data is a query and your "somedata" field is a single column. Try this:
SELECT REPLACE( somedata_column, ',', '<br />' )
FROM mytable
You don't say what version of Apex. In Apex 4.x, the column would need to be set to a Standard Report Column, which would stop Apex from the <br> elements. I forget what the column type is in Apex 5.x.
Check below sample query which converts coma separated list data into rows
SELECT substr( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,',
( case when rownum = 1 then 1
else instr( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,', ',', 1, rownum - 1 ) + 1
end ),
instr( substr( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,',
( case when rownum = 1 then 1
else instr( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,', ',', 1, rownum - 1 ) + 1
end )
), ',' ) - 1
) as data
FROM dual
CONNECT BY LEVEL <= length( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,' ) - length ( replace('1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,', ',') )
Hope this will help you!

How to split varchar in oracle

I have a procedure in where I am taking the input parameters as array of strings.
This String contains like 5-Deal deleted
I want to split this varchar into 5 and Deal deleted.
Here split conditions is -
Try using regexp_substr:
select regexp_substr ('5-Deal deleted' , '[^-]+', 1, rownum) split
from dual
connect by level <= length (regexp_replace ('5-Deal deleted' , '[^-]+')) + 1;
Then you can use BULK COLLECT INTO for store into a variable
For such a simple task, I would go with SUBSTR and INSTR. REGULAR EXPRESSION would be too much resource consuming.
INSTR would find the position of -, i.e. hyphen, and SUBSTR would pick the required portion of the string.
Or,
If your example data is what it looks like for all the rows, then, just extract DIGIT and ALPHA from the string and just concatenate them. This would obviously need REGULAR EXPRESSION.
Try this:
SELECT SUBSTR ('5-Deal deleted', 1, INSTR ('5-Deal deleted', '-') - 1)
AS FIRST,
SUBSTR ('5-Deal deleted', INSTR ('5-Deal deleted', '-') + 1) AS second
FROM DUAL

String Manipulation in Oracle 11g

I am currently working on an exercise that will display a certain text, say TESTTEMP, from a series of characters stored in a column. Example of the string:
PROGRAMNAME|MAX2112ETI_L;PROGRAMREV|L;TESTOPTION|FR;TESTTEMP|25;STD IPH|528.63436123348
Now what I need is to extract the text past the string TESTTEMP| and before the ;. What I did is I extracted all the text past TESTTEMP| and just get the first two character. Unfortunately, this will not be possible since there are cases where in the TESTTEMP| has 3 characters. Is there a way for me to be able to do this? Below is what I have so far:
SELECT
SUBSTR(SUBSTR(value, INSTR(value, ';TESTTEMP|')+10), 1, 2) invalue
FROM
(
SELECT
'PROGRAMNAME|MAX2112ETI_L;PROGRAMREV|L;TESTOPTION|FR;TESTTEMP|25;STD IPH|528.63436123348' VALUE
FROM dual
)t;
Find the index of ; in the substring and use that as index instead of hard coding it to 2.
SELECT
SUBSTR(SUBSTR(value, INSTR(value, ';TESTTEMP|')+10), 1, INSTR(SUBSTR(value, INSTR(value, ';TESTTEMP|')+10), ';')-1) invalue
FROM
(
SELECT
'PROGRAMNAME|MAX2112ETI_L;PROGRAMREV|L;TESTOPTION|FR;TESTTEMP|25;STD IPH|528.63436123348' VALUE
FROM dual
)t;
If that look messy, you can even write it like this
SELECT
SUBSTR(VALUE, 1, INSTR(VALUE, ';') - 1) invalue
FROM
(
SELECT
SUBSTR(VALUE, INSTR(VALUE, ';TESTTEMP|') + 10)
VALUE
FROM (
SELECT
'PROGRAMNAME|MAX2112ETI_L;PROGRAMREV|L;TESTOPTION|FR;TESTTEMP|25;STD IPH|528.63436123348'
VALUE
FROM dual) t
)t;
Based on comments - if you want to know if the string was found or not, use this query. FOUND will be greater than 0 if the string is found.
SELECT
SUBSTR(VALUE, 1, INSTR(VALUE, ';')-1) invalue, FOUND
FROM
(
SELECT
SUBSTR(VALUE, INSTR(VALUE, SEARCHSTRING)+10) VALUE, INSTR(VALUE, SEARCHSTRING) FOUND
FROM (
SELECT
'PROGRAMNAME|MAX2112ETI_L;PROGRAMREV|L;TESTOPTION|FR;TESTTEMP|25;STD IPH|528.63436123348'
VALUE,
';TESTTEMP|' SEARCHSTRING
FROM dual) t
)t;

Resources