substr: how to exclude special characters like } " _ etc - sql

This is the message in the REMARKS column of my table
{"StatusCode":"0","StatusDescription":"","message":"","transactionid":"404897688","enddate":"04/03/2017","formula":"ACCESS"}_SUBS
{"StatusCode":"0","StatusDescription":"","message":"","transactionid":"404894098","enddate":"04/03/2017","formula":"EVASION"}_SUBN
{"StatusCode":"0","StatusDescription":"","message":"","transactionid":"404889188","enddate":"05/03/2017","formula":"LES CHAINES CANAL+"}_SUBS
{"StatusCode":"0","StatusDescription":"","message":"","transactionid":"404880515","enddate":"06/03/2017","formula":"EVASION+"}_SUBS
I am using this in my query
substr(remarks, (instr(remarks,'formula') + 10), 18) FORMULA
But i am also getting the special characters } " _ because EVASION+ LES CHAINES CANAL+ EVASION ACCESS are not of the same length.
Can someone explain how to exclude those special characters and get only the names displayed under FORMULA column.
thanks

Here is a solution using just the standard substr and instr functions (no regular expressions):
select substr( remarks, instr(remarks, '"formula":"') + 11,
instr(substr(remarks, instr(remarks, '"formula":"') + 11), '"') - 1 )
from inputs;

If the remarks column is just a string, you might try:
with x as (
select '{"StatusCode":"0","StatusDescription":"","message":"","transactionid":"404897688","enddate":"04/03/2017","formula":"ACCESS"}_SUBS' as remarks
from dual
)
select
regexp_substr(remarks, '"formula":"(.*?)"',1,1,'i',1)
from x;
Output:
ACCESS

Related

changing the sub-string delimiter in a regex, does not return same result

I have a problem with understanding a regex.
I have the following string:
aaa'dd?'d'xxx'
In this string, the
'
is substring delimiter and
?
is a escape character for the
'
.
In Oracle SQL, I have a sentence which splits my string in substrings, based on substring delimiter:
select replace(
regexp_substr(q'[aaa'dd?'d'xxx']', '(.*?[^?])(''|$)', 1, level, null, 1),
'?''',
'''') as result
FROM dual
connect by level <= regexp_count(q'[aaa'dd?'d'xxx']', '(.*?[^?])(''|$)');
In this case, the result is:
aaa
dd'd
xxx
... which is correct.
My problem comes from the fact that I want to change the sub-string delimiter from
'
into
+
.
In this case, the main string becomes
aaa+dd?+d+xxx+
I modified the SQL statement in:
SELECT REPLACE(
regexp_substr(q'[aaa+dd?+d+xxx+]', '(.*?[^?])(+|$)', 1, level, null, 1),
'?''',
'''') as result
FROM dual
connect by level <= regexp_count(q'[aaa+dd?+d+xxx+]', '(.*?[^?])(+|$)');
... and the result is different:
a
a
a
+
d
d
?+
d
+
x
x
x
+
Can you point me what am I doing wrong in my modified script in order to get same result, please?
In regexp + means 1 or more of the preceding pattern. Try escaping the + with \ making your regexp '(.*?[^?])(\+|$)'

Concatenating values from a variable in Oracle SQL

I have a problem with my SQL report. I passed values from my column to a variable. Now, I need to show values from my variable ('#{EMPLOYEES}') with a ' ' at the beginning and ending of each name. To clear this out:
My variable holds the values like one long string:
('Employee1, Employee2, Employee3, Employee4')
And I need it to hold the values like separate strings:
('Employee1','Employee2','Employee3','Employee4')
Can this be somehow done by CONCAT function? If so, then how?
Since you're trying to split a comma-separated string and pass the output into an IN-condition in oracle (as you clarify in the comments), you can follow the example in https://blogs.oracle.com/aramamoo/how-to-split-comma-separated-string-and-pass-to-in-clause-of-select-statement
Try this:
select chr(39) || 'word' ||chr(39) from dual
Late to the game here, I enjoy playing with Common Table Expressions. Here is how you can split your string using a CTE:
WITH
setup AS (SELECT 'a,b,c,d' letter FROM DUAL),
initialize AS
(SELECT letter || ',' AS letter
FROM setup),
splitset (letter, rest) AS
(SELECT SUBSTR (letter, 1, INSTR (letter, ',') - 1) AS letter
, SUBSTR (letter, INSTR (letter, ',') + 1) AS rest
FROM initialize
UNION ALL
SELECT SUBSTR (rest, 1, INSTR (rest, ',') - 1) AS letter
, SUBSTR (rest, INSTR (rest, ',') + 1) AS rest
FROM splitset
WHERE rest IS NOT NULL)
SELECT letter
FROM splitset;
Setup is just setting up some sample data, your code would start with 'intialize'.

How to extract the number from a string using Oracle?

I have a string as follows: first, last (123456) the expected result should be 123456. Could someone help me in which direction should I proceed using Oracle?
It will depend on the actual pattern you care about (I assume "first" and "last" aren't literal hard-coded strings), but you will probably want to use regexp_substr.
For example, this matches anything between two brackets (which will work for your example), but you might need more sophisticated criteria if your actual examples have multiple brackets or something.
SELECT regexp_substr(COLUMN_NAME, '\(([^\)]*)\)', 1, 1, 'i', 1)
FROM TABLE_NAME
Your question is ambiguous and needs clarification. Based on your comment it appears you want to select the six digits after the left bracket. You can use the Oracle instr function to find the position of a character in a string, and then feed that into the substr to select your text.
select substr(mycol, instr(mycol, '(') + 1, 6) from mytable
Or if there are a varying number of digits between the brackets:
select substr(mycol, instr(mycol, '(') + 1, instr(mycol, ')') - instr(mycol, '(') - 1) from mytable
Find the last ( and get the sub-string after without the trailing ) and convert that to a number:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE test ( str ) AS
SELECT 'first, last (123456)' FROM DUAL UNION ALL
SELECT 'john, doe (jr) (987654321)' FROM DUAL;
Query 1:
SELECT TO_NUMBER(
TRIM(
TRAILING ')' FROM
SUBSTR(
str,
INSTR( str, '(', -1 ) + 1
)
)
) AS value
FROM test
Results:
| VALUE |
|-----------|
| 123456 |
| 987654321 |

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

How do I concat strings from different columns and search using this expression

I have a lot of values like '1#1#15'
I need to build sql for DB (legasy FoxPro) request like this:
SELECT ba1 AS mest, bd1 AS Ukpg, be1 AS well, be12 AS probur, be13 AS iskust, be6 AS burDate
FROM ksmest
WHERE ((ba1 + '#' + bd1 + '#' + be1) IN ('1#1#15', '3#1#15'))
ba1, bd1 and be1 is numbers (not string).
As you see, I need to concat strings from different columns and search using this expression. My request is not works, it is just a sample.
Is it possible to solve? Or may be another ways?
You can combine the relevant columns into an aggregate field in the SELECT statement, converting them to string in the process - see 'mycolumn' in this example. Also your syntax there looks more like T-SQL, for VFP you would use INLIST()
select ;
ba1 AS mest, bd1 AS Ukpg, be1 AS well, be12 AS probur, be13 AS iskust, be6 AS burDate ;
alltrim(str(ba1, 12, 0)) + "#" + alltrim(str(bd1, 12, 0)) ;
+ "#" + alltrim(str(be1, 12, 0)) as mycolumn;
from ksmest ;
where inlist(mycolumn, "1#1#15", "3#1#15")