Oracle - How to extract delimited string - sql

I have a sample String as below,
A|SDFR|RESTA|PRET|PRUQA
B|121|BBCTRI|9ALFA|DEV|5AS
I want to extract the part that is coming after send delimiter,
Expected,
RESTA|PRET|PRUQA
BBCTRI|9ALFA|DEV|5AS
What i got is just extracting single characters regexp_substr

Assuming you mean after the second delimiter, you don't need to use regular expressions for this; you can use the basic ]substr()](http://docs.oracle.com/database/121/SQLRF/functions196.htm) function, getting the starting position with instr():
substr(<your_string>, instr(<your_string>, '|', 1, 2) + 1)
The third argument to instr() says you want the second occurrence; the second argument says you're starting from position 1. That then points to the second delimiter, and you want to start at the next character after that delimiter, so have to add 1.
Demo:
with t (str) as (
select 'A|SDFR|RESTA|PRET|PRUQA' from dual
union all select 'B|121|BBCTRI|9ALFA|DEV|5AS' from dual
)
select substr(str, instr(str, '|', 1, 2) + 1)
from t;
SUBSTR(STR,INSTR(STR,'|',1
--------------------------
RESTA|PRET|PRUQA
BBCTRI|9ALFA|DEV|5AS

try this:
substr(string, instr(string, '|', 1, 2)+1)

Maybe like this;
with a as (select 'B|121|BBCTRI|9ALFA|DEV|5AS' test from dual)
select substr(test,instr(test,'|',3)+1) from a

Related

How to get the substring before the last occurrence of a character?

In oracle I have a string that looks like this:
\A\B\C\D
Now I would like to get the substring before the last occurrence of \. I don't know how long the string in total will be, as A, B, C and D can differ in length, and the number of \ might vary as well.
What is the most efficient way to get \A\B\C\?
You could use a combination of instr (with -1) and substr:
with data as (select '\A\B\C\D' str from dual)
select str, substr(str, 1, instr(str, '\', -1))
from data;
You can use regexp_replace():
select regexp_replace(str, '[^\\]+$', '')
Here is a db<>fiddle.
A solution through a Regex function might be
SELECT REGEXP_REPLACE(str, '[^\\]+$')
FROM t
Demo
if it is desired that that last occurrence of the \ character be skipped also, then this might work.
REGEXP_SUBSTR (str,'(.*)(\.*$)',
1,
1,
NULL,
1)
this was learned from this post https://stackoverflow.com/a/63458155/18021603

Get the data from a string between double quotes in Oracle

I have a string with double quotes inside.
EG:
<cosmtio :ff "intermit"ksks>
I need the data between the ""
I have tried the regexp_substr but still couldn't get the value between double-quotes.
We could try using REGEXP_REPLACE here:
SELECT
string,
REGEXP_REPLACE(string, '.*"([^"]+)".*', '\1') AS quoted_term
FROM yourTable;
Data:
WITH yourTable AS (
SELECT '<cosmtio :ff "intermit"ksks>' AS string FROM dual
)
Demo
Another option, using REGEXP_SUBSTR:
SELECT
string,
TRIM(BOTH '"' FROM REGEXP_SUBSTR(string, '".*"'))
FROM yourTable;
But this approach requires nesting two function calls, which means it might not outperform the REGEXP_REPLACE version.
You need to use REGEXP_SUBSTR:
SELECT REGEXP_SUBSTR('<cosmtio :ff "intermit"ksks>', '"([^"]+)"', 1, 1, NULL, 1) AS Result FROM DUAL
See the online demo.
The regex is simple: "([^"]+)" matches ", then captures any 1+ chars other than " into Group 1 and then matches ". The last argument is 1 telling Oracle REGEXP_SUBSTR to return the Group 1 values. The first (position) and the second (occurrence) arguments are default, 1. NULL means no specific options need to be passed to the regex engine.
You can try the following:
SELECT REGEXP_REPLACE('<cosmtio :ff "intermit"ksks>', '^[^"]*("([^"]*)")?.*', '\2') FROM dual
It is possible with regexp_substr as following:
Select
regexp_substr('<cosmtio :ff "intermit"ksks>', '[^"]+', 1, 2)
from dual;
Cheers!!

Get string until character Oracle SQL

How to get string before character?
I need to get string before ; in Oracle SQL.
For example:
147739 - Blablabla ; Blublublu
Needed output:
147739 - Blablabla
My code so far:
SELECT
UPPER(CONVERT(REGEXP_REPLACE(SUBSTR(HISTORICO, INSTR(HISTORICO, 'Doc') + 4), 'S/A', 'SA'), 'US7ASCII'))
FROM
GEQ_GL_CONC_CONTABIL_FRETES_V
WHERE
periodo = '$Periodo$' AND livro = 'ESMALTEC_FISCAL'
I want the whole string up to ;
We can use a combination of SUBSTR and INSTR to achieve this;
SELECT SUBSTR(FIELD_NAME,1,INSTR(FIELD_NAME,';', 1, 1)-1) FROM TABLE_NAME;
The first argument to SUBSTR is the position in the field value from which we want to start (1 = at the beginning), the second argument is the length of the substring we want to read, here it is synonymous with the position of ';' -1.
The third and fourth arguments to INSTR are where to start searching for ';' and the count we are interested in. In our example that is from the beginning (1) and the first occurence (again 1).
You could try using substr() and instr()
select SUBSTR(my_col, 0, INSTR(my_col, ';')-1)
from my_table
select SUBSTR(' Blablabla ; Blublublu', 0, INSTR('A Blablabla ; Blublublu', ';')-1)
from dual
A few alternatives using REGEXP
The result with each solution depends of how uniform your data is
WITH tbl
AS (
SELECT '147739 - Blablabla ; Blublublu' str
FROM DUAL
)
SELECT TRIM(REGEXP_SUBSTR(str, '([[:alnum:]]|-| )*')) AS SOLUTION_1
, REGEXP_SUBSTR(str, '[[:digit:]]*( )?(-)?( )?[[:alpha:]]*') AS SOLUTION_2
, REGEXP_SUBSTR(str, '[[:digit:]]*( |-)*[[:alpha:]]*') AS SOLUTION_3
FROM tbl;

How to replace more than one character in oracle?

How to replace multiple whole characters, except those in combinations...?
The below code replaces multiple characters, but it also disturbing those in combinations.
SELECT regexp_replace('a,ca,va,ea,r,y,q,b,g','(a|y|q|g)','X') RESULT FROM dual;
Current output:
RESULT
--------------------
X,cX,vX,eX,r,X,X,b,X
Expected output:
RESULT
------------------------
'X,ca,va,ea,r,X,X,b,X
I just want to replace only separate whole characters('a','y','q','g'), but not the 1 in combinations('ca','va','ea')...
Because you are delimiting with a comma ',' you can combine that like ',a,'
and this will replace only single a's.
you can try follows:
with t as
(
select 'a,ca,va,ea,r,y,q,b,g' str
from dual
)
select substr(sys_connect_by_path(regexp_replace(regexp_substr(str, '[^,]+', 1, level), '^(a|y|q|g)$', 'X'), ','), 2) as str
from t
where connect_by_isleaf = 1
connect by level <= length(regexp_replace(str, '[^,]*')) + 1;
Sadly oracle doesn´t support lookahead and lookbehind. But this is a solution i came up with.
SELECT regexp_replace
(regexp_replace
('a,ca,va,ea,r,y,q,b,g',
'^[ayqg](,)|(,)[ayqg](,)|(,)[ayqg]$',
'\2\4X\1\3'),'(,)[ayqg](,)','\1X\2')
RESULT FROM dual;
I had to use the regexp twice sadly, since it doesn´t find two similar values following after each other and replacing it. ..,a,y,.. is getting replaced as ..,X,y,... So the second call replaces the missing [ayqg] with the exact values. In the first inner regexp call replaces the first and last values.
Maybe this could be simplified into one expression, but i am not that conform with the regex from oracle.
As a explanation i am grouping the commata and basicly replace every ,[ayqg], with ,X, by backreferencing the commata
You would look for word boundaries, which is \b, and which is unfortunately not supported by Oracle's regexp_replace.
So let's look for a non-word character \W or the beginning ^ or ending $ of the text.
select
regexp_replace('a,ca,va,ea,r,y,q,b,g','(^|$|\W)(a|y|q|g)(^|$|\W)','\1X\3') as result
from dual;
In order to not remove the non-word characters, we must have them in the replace string: \1 for the expression in the first parenteses, \3 for the ones in the third. Thus we only change the expression in the second parentheses, which is a, y, q or g, with X.
Unfortunately above gives
X,ca,va,ea,r,X,q,b,X
The q was not replaced, because we recognize ',y,' thus being positioned a 'g,' whereas we'd need to be positioned at ',g,' to recognize g as a word, too.
So we need to replace in iterations (i.e. recursively):
with results(txt, num) as
(
select 'a,ca,va,ea,r,y,q,b,g' as txt, 0 as num from dual
union all
select regexp_replace(txt, '(^|$|\W)(a|y|q|g)(^|$|\W)','\1X\3'), num + 1 as num
from results
where txt <> regexp_replace(txt, '(^|$|\W)(a|y|q|g)(^|$|\W)','\1X\3')
)
select max(txt) keep (dense_rank last order by num) as result
from results;
EDIT: Kevin Esche is right; of course one has to do it only twice. Hence you can also do:
select
regexp_replace(txt, search_str, replace_str) as result
from
(
select
regexp_replace(txt, search_str, replace_str) as txt, search_str, replace_str
from
(
select
'a,ca,va,ea,r,y,q,y,q,b,g' as txt,
'(^|$|\W)(a|y|q|g)(^|$|\W)' as search_str,
'\1X\3' as replace_str
from dual
)
);
with replaced_values as (
SELECT case when length(val)=1 then regexp_replace(val,'(a|y|q|g)','X') else val end new_val, lvl
from (
SELECT regexp_substr('a,ca,va,ea,r,y,q,b,g','[^,]+', 1, LEVEL) val, level lvl FROM dual
connect by regexp_substr('a,ca,va,ea,r,y,q,b,g','[^,]+',1, LEVEL) is not null
) all_values
)
select lISTAGG(new_val, ',') WITHIN GROUP (ORDER BY lvl) RESULT
from replaced_values
This statement pivots data into rows and replaces only lines wich contains one character.
Data are then unpivoted in one rows
This sql works also with empty entries like 'a,,,b,c' and more complex regular expressions:
with t as
(select ',a,,ca,va,ea,bbb,ba,r,y,q,b,g,,,' as str,
',' as delimiter,
'(a|y|q|g|ea|[b]*)' as regexp_expr,
'X' as replace_expr
from dual)
(select substr (sys_connect_by_path(regexp_replace(substr(str,
decode(level - 1, 0, 0, instr(str, ',', 1, level - 1)) + 1,
decode(instr(str, ',', 1, level),
0,
length(str),
instr(str, ',', 1, level) - 1) -
decode(level - 1, 0, 0, instr(str, ',', 1, level - 1))),
'^' || regexp_expr || '$',
replace_expr), ','), 2)
from t
where connect_by_isleaf = 1
connect by level <= length(regexp_replace(str, '[^'|| delimiter||']')) + 1)
Result
,X,,ca,va,X,X,ba,r,X,X,X,X,,,
Don't Know much Oracle, but I would have thought something like this could work. Assuming the delimiter is always a comma.
SELECT
regexp_replace(regexp_replace(regexp_replace(regexp_replace(regexp_replace('a,ca,va,ea,r,y,q,b,g','(,a,|,y,|,q,|,g,)',',X,') ,'(,a,|,y,|,q,|,g,)',',X,'), '(^a,|^y,|^q,|^g,)','X,'), '(,a$|,y$|,q$|,g$)',',X'), '(^a$|^y$|^q$|^g$)','X')
RESULT FROM test;
The first two parts replaces a single character in commas in the middle, the third part gets those at the start of the string, the fourth is for the end of the string and the fifth is for when then string has just one character.
This answer might will be simplifiable by advanced Regexp use.
How i can replace words?
RS & OS ===> D, LS & IS ==== >
SECTION_ID Output required
1-LS-1991 1-P-1991
1-IS-1991 1-P-1991
1-RS-1991 1- D- 1991
1-OS-1991 1-D-1991

Split String by delimiter position using oracle SQL

I have a string and I would like to split that string by delimiter at a certain position.
For example, my String is F/P/O and the result I am looking for is:
Therefore, I would like to separate the string by the furthest delimiter.
Note: some of my strings are F/O also for which my SQL below works fine and returns desired result.
The SQL I wrote is as follows:
SELECT Substr('F/P/O', 1, Instr('F/P/O', '/') - 1) part1,
Substr('F/P/O', Instr('F/P/O', '/') + 1) part2
FROM dual
and the result is:
Why is this happening and how can I fix it?
Therefore, I would like to separate the string by the furthest delimiter.
I know this is an old question, but this is a simple requirement for which SUBSTR and INSTR would suffice. REGEXP are still slower and CPU intensive operations than the old subtsr and instr functions.
SQL> WITH DATA AS
2 ( SELECT 'F/P/O' str FROM dual
3 )
4 SELECT SUBSTR(str, 1, Instr(str, '/', -1, 1) -1) part1,
5 SUBSTR(str, Instr(str, '/', -1, 1) +1) part2
6 FROM DATA
7 /
PART1 PART2
----- -----
F/P O
As you said you want the furthest delimiter, it would mean the first delimiter from the reverse.
You approach was fine, but you were missing the start_position in INSTR. If the start_position is negative, the INSTR function counts back start_position number of characters from the end of string and then searches towards the beginning of string.
You want to use regexp_substr() for this. This should work for your example:
select regexp_substr(val, '[^/]+/[^/]+', 1, 1) as part1,
regexp_substr(val, '[^/]+$', 1, 1) as part2
from (select 'F/P/O' as val from dual) t
Here, by the way, is the SQL Fiddle.
Oops. I missed the part of the question where it says the last delimiter. For that, we can use regex_replace() for the first part:
select regexp_replace(val, '/[^/]+$', '', 1, 1) as part1,
regexp_substr(val, '[^/]+$', 1, 1) as part2
from (select 'F/P/O' as val from dual) t
And here is this corresponding SQL Fiddle.