I have one key/value comma separated string, and another keys-labels comma separated string as per the following example (ID/Name/Age):
key/value: 1=101,2=John,3=43
keys labels: ID,Name,Age
Result: ID=101,Name=John,Age=43
Is there a built in Oracle function (ex:regexp_replace) that can accomplish this?
I don't think there's a simple function, no. regexp_replace only replaces one value at a time, so to replace 3 values in a string, you need to loop over it 3 times.
It's possible to split the strings, use a connect by loop to replace each value, and then put the pairs back together, but it is a bit awkward. Example:
with t as (select '1=101,2=John,3=43' as pairs, 'ID,Name,Age' as labels from dual)
select
listagg(
regexp_replace(regexp_substr(pairs, '[^,]+', 1, level),
'\d+(.+)',
regexp_substr(labels, '[^,]+', 1, level) || '\1')
,',')
from t
connect by regexp_substr(pairs, '[^,]+', 1, level) is not null
See the link above for how to modify this query for a table with multiple rows and a unique key ID.
if the key values and labels length are fixed then you can get the result with replace function itself
select REPLACE(REPLACE( REPLACE('1=101,2=John,3=43', '1=','ID='),'2=','Name=') ,'3=','Age=') as finresult from dual ;
OUTPUT:
FINRESULT
ID=101,Name=John,Age=43
Related
My Parameter to a procedure lv_ip := 'MNS-GC%|CS,MIB-TE%|DC'
My cursor query should search for records that start with 'MNS-GC%' and 'MIB-TE%'.
Select id, date,program,program_start_date
from table_1
where program like 'MNS-GC%' or program LIKE 'MIB-TE%'
Please suggest ways to read it from the parameter and an alternative to LIKE.
Since you mention you want to preserve what's on the right side of the pipe, and want to be able to process parameters dynamically, here's a way to parse multi-delimited data that could give you some ideas using a CTE.
The table called 'tbl' just sets up your original data. tbl_comma contains that data split on the comma. The final query splits that data into name/value pairs.
Hopefully this will help give you some ideas even though it's not the exact answer you are looking for.
COLUMN ID FORMAT a3
COLUMN PROGRAM FORMAT a10
COLUMN part2 FORMAT a6
-- Original data
WITH tbl(ID, DATA) AS (
SELECT 1, 'MNS-GC%|CS,MIB-TE%|DC' FROM dual UNION ALL
SELECT 2, 'MNS-GC%|CS,MIB-TE%|DC,MIB-TA%|AB,MIB-TB%|BC' FROM dual
),
tbl_comma(ID, CASE) AS (
SELECT ID,
REGEXP_SUBSTR(DATA, '(.*?)(,|$)', 1, LEVEL, NULL, 1) CASE
FROM tbl
CONNECT BY REGEXP_SUBSTR(DATA, '(.*?)(,|$)', 1, LEVEL) IS NOT NULL
AND PRIOR ID = ID
AND PRIOR SYS_GUID() IS NOT NULL
)
--SELECT * FROM tbl_comma;
-- Parse into name/value pairs
SELECT ID,
REGEXP_REPLACE(CASE, '^(.*)\|.*', '\1') PROGRAM,
REGEXP_REPLACE(CASE, '.*\|(.*)$', '\1') PART2
FROM tbl_comma;
ID PROGRAM PART2
--- ---------- ------
1 MNS-GC% CS
1 MIB-TE% DC
2 MNS-GC% CS
2 MIB-TE% DC
2 MIB-TA% AB
2 MIB-TB% BC
6 rows selected.
If you're stuck with that input and the structure is fixed, with each comma-separated element having a pipe-delimited value, you could possibly convert that string to a regular expression pattern, and then use regexp_like to pattern-match:
select id, date, program, program_start_date
from table_1
where regexp_like(
program,
'^(' || rtrim(regexp_replace(lv_ip, '%\|.*?(,|$)', '|'), '|') || ')')
With your example parameter, the
'^(' || rtrim(regexp_replace(lv_ip, '%\|.*?(,|$)', '|'), '|') || ')'
would generate the pattern
^(MNS-GC|MIB-TE)
i.e. looking for either of those strings at the start of the program value.
db<>fiddle
Alternatively you could split the input up yourself, with instr and substr, and - since the number of elements may vary - create a dynamic query using them. That might be faster than using regular expression, but might be harder to maintain.
What would the regexp be to match CS|DC
It depends how you plan to use those values, but if you're looking for some column exactly matching one of them, then you could do something similar with:
'^(' || ltrim(regexp_replace(l_ip, '(^|,)[^|]*', null), '|') || ')$'
which with your input string would generate the pattern
^(CS|DC)$
But if you need to match the corresponding values as pairs - so the equivalent of something like:
where (program like 'MNS-GC%' and some_col = 'CS')
or (program like 'MIB-TE%' and some_col = 'DC')
... then you'd need to extract them as pairs, as #Gary_W has shown.
'HEADER|N1000|E1001|N1002|E1003|N1004|N1005'
'HEADER|N156|E1|N7|E122|N4|E5'
'HEADER|E0|E1|E2|E3|E4|E5'
'HEADER|N0|N1|N2|N3|N4|N5'
'HEADER|N125'
How to extract the numbers in comma-separated format from this stringS?
Expected result:
1000,1001,1002,1003,1004,1005
How to extract the numbers with N or E as suffix/prefix ie.
N1000
Expected result:
1000,1002,1004,1005
below regex does not return the result needed. But I want some thing like this
select REGEXP_REPLACE(REGEXP_REPLACE('HEADER|N1000|E1001|N1002|E1003|N1004|N1005', '.*?(\d+)', '\1,'), ',?\.*$', '') from dual
the problem here is
when i want numbers with E OR N
select REGEXP_REPLACE(REGEXP_REPLACE('HEADER|N1000|E1001|N1002|E1003|N1004|N1005', '.*?N(\d+)', '\1,'), ',?\.*$', '') from dual
select REGEXP_REPLACE(REGEXP_REPLACE('HEADER|N1000|E1001|N1002|E1003|N1004|N1005', '.*?E(\d+)', '\1,'), ',?\.*$', '') from dual
they give good results for this scenerio
but when i input 'HEADER|N1000|E1001' it gives wrong answer plzzz verify and correct it
Update
Based on the changes to the question, the original answer is not valid. Instead, the solution is considerably more complex, using a hierarchical query to extract all the numbers from the string and then LISTAGG to put back together a list of numbers extracted from each string. To extract all numbers we use this query:
WITH cte AS (
SELECT DISTINCT data, level AS l, REGEXP_SUBSTR(data, '[NE]\d+', 1, level) AS num FROM test
CONNECT BY REGEXP_SUBSTR(data, '[NE]\d+', 1, level) IS NOT NULL
)
SELECT data, LISTAGG(SUBSTR(num, 2), ',') WITHIN GROUP (ORDER BY l) AS "All numbers"
FROM cte
GROUP BY data
Output (for the new sample data):
DATA All numbers
HEADER|E0|E1|E2|E3|E4|E5 0,1,2,3,4,5
HEADER|N0|N1|N2|N3|N4|N5 0,1,2,3,4,5
HEADER|N1000|E1001|N1002|E1003|N1004|N1005 1000,1001,1002,1003,1004,1005
HEADER|N125 125
HEADER|N156|E1|N7|E122|N4|E5 156,1,7,122,4,5
To select only numbers beginning with E, we modify the query to replace the [EN] in the REGEXP_SUBSTR expressions with just E i.e.
SELECT DISTINCT data, level AS l, REGEXP_SUBSTR(data, 'E\d+', 1, level) AS num FROM test
CONNECT BY REGEXP_SUBSTR(data, 'E\d+', 1, level) IS NOT NULL
Output:
DATA E-numbers
HEADER|E0|E1|E2|E3|E4|E5 0,1,2,3,4,5
HEADER|N0|N1|N2|N3|N4|N5
HEADER|N1000|E1001|N1002|E1003|N1004|N1005 1001,1003
HEADER|N125
HEADER|N156|E1|N7|E122|N4|E5 1,122,5
A similar change can be made to extract numbers commencing with N.
Demo on dbfiddle
Original Answer
One way to achieve your desired result is to replace a string of characters leading up to a number with that number and a comma, and then replace any characters from the last ,| to the end of string from the result:
SELECT REGEXP_REPLACE(REGEXP_REPLACE('HEADER|N1000|E1001|N1002|E1003|N1004|N1005|', '.*?(\d+)', '\1,'), ',?\|.*$', '') FROM dual
Output:
1000,1001,1002,1003,1004,1005
To only output the numbers beginning with N, we add that to the prefix string before the capture group:
SELECT REGEXP_REPLACE(REGEXP_REPLACE('HEADER|N1000|E1001|N1002|E1003|N1004|N1005|', '.*?N(\d+)', '\1,'), ',?\|.*$', '') FROM dual
Output:
1000,1002,1004,1005
To only output the numbers beginning with E, we add that to the prefix string before the capture group:
SELECT REGEXP_REPLACE(REGEXP_REPLACE('HEADER|N1000|E1001|N1002|E1003|N1004|N1005|', '.*?E(\d+)', '\1,'), ',?\|.*$', '') FROM dual
Output:
1001,1003
Demo on dbfiddle
I don't know what DBMS you are using, but here's one way to do it in Postgres:
WITH cte AS (
SELECT CAST('HEADER|N1000|E1001|N1002|E1003|N1004|N1005|' AS VARCHAR(1000)) AS myValue
)
SELECT SUBSTRING(MyVal FROM 2)
FROM (
SELECT REGEXP_SPLIT_TO_TABLE(myValue,'\|') MyVal
FROM cte
) src
WHERE SUBSTRING(MyVal FROM 1 FOR 1) = 'N'
;
SQL Fiddle
As Far as I have understood the question , you want to extract substrings starting with N from the string, You can try following (And then you can merge the output seperated by commas if needed)
select REPLACE(value, 'N', '')
from STRING_SPLIT('HEADER|N1000|E1001|N1002|E1003|N1004|N1005|', '|')
where value like 'N%'
OutPut :
1000
1002
1004
1005
I have an requirement in my project where I need to split the url and extract variable from it , url is a column in one of the table. url looks like this
Input Row
"http://example.com/wps/myportal/example/viewer?mstrContentType=Report& mstrId=15F4AC9C4453E5F75456438F732F7B8C&contentName=7.++CTI+Session+Order+Counts&proj=System+Xr+-+DMDR/CW&workspaceName=&woId=&vendorId=Microstrategy"
I need o/p as follows to compare with target column as follows
ContentType=Report
mstrId=15F4AC9C4453E5F75456438F732F7B8C
contentName=7.++CTI+Session+Order+Counts
etc...
I can use only Oracle , should not use programatic language.
Thanks.
Here all you need to use, the REGEXP_SUBSTR as below.
create table test1(col1 varchar2(4000));
insert into test1 values(
'http://example.com/wps/myportal/example/viewer?mstrContentType=Report& mstrId=15F4AC9C4453E5F75456438F732F7B8C&contentName=7.++CTI+Session+Order+Counts&proj=System+Xr+-+DMDR/CW&workspaceName=&woId=&vendorId=Microstrategy'
);
select regexp_substr(col1,'[^?"]+',1,2) ss from test1;
select trim(regexp_substr(regexp_substr(col1,'[^?"]+',1,2),'[^&]+', 1, level)) as output_data from test1
connect by regexp_substr(regexp_substr(col1,'[^?"]+',1,2), '[^&]+', 1, level) is not null;
OUTPUT_DATA
mstrContentType=Report
mstrId=15F4AC9C4453E5F75456438F732F7B8C
contentName=7.++CTI+Session+Order+Counts
proj=System+Xr+-+DMDR/CW
workspaceName=
woId=
vendorId=Microstrategy
If your url strings follow a predictable format, you can capture matching portions of the url with regexp_replace
select regexp_replace(
regexp_substr(url, '[?&]mstrContentType=([^&]*)')
'[^=]*=(.*)'
'\1'
)
This will return the value of the mstrContentType parameter - the substring to the right of the equals sign but before the next parameter.
So, I have a piece of simple SQL like:
select
REGEXP_SUBSTR (randomcol, '[^|]+', 1, 2)
||'|'|| REGEXP_SUBSTR (randomcol, '[^|]+', 1, 3)
||'|'|| REGEXP_SUBSTR (randomcol, '[^|]+', 1, 4)
from table1 where ADDTL_DETAIL_INFO is not null and module_key='01-07-2016 00:00:00/2212/ 1';
The idea is to get the pipe separated values present in the randomcol column where the value present is:
~custom|HELLO1||HELLO3
So I need the values like HELLO1,whitespace (as there is no value between the second pipe and the third pipe) and HELLO3.
But when I ran the above query it returns as:
HELLO1|HELLO3|
and the white space is gone. I need this white space to retain. So what am I doing wrong here?
Regex of the form '[^|]+' does not work with NULL list elements and should be avoided! See this post for more info: Split comma separated values to columns in Oracle
Use this form instead:
select regexp_substr('1,2,3,,5,6', '(.*?)(,|$)', 1, 5, NULL, 1) from dual;
Which can be read as "get the 1st remembered group of the 5th occurrence of the set of characters ending with a comma or the end of the line".
So for your 4th element, you would use this to preserve the NULL in element 3 (assuming you want to build it by separate elements and not just grab the string from the character after the first separator to the end):
...
REGEXP_SUBSTR (addtl_detail_info, '(.*?)(\||$)', 1, 2) ||
REGEXP_SUBSTR (addtl_detail_info, '(.*?)(\||$)', 1, 3) ||
REGEXP_SUBSTR (addtl_detail_info, '(.*?)(\||$)', 1, 4)
...
You know, this may be easier. Just grab everything after the first pipe:
SQL> select REGEXP_replace('~custom|HELLO1||HELLO3', '^.*?\|(.*)', '\1') result
from dual;
RESULT
--------------
HELLO1||HELLO3
SQL>
The parenthesis surround what you want to "remember" and the replace string references the 1st remembered group with "\1".
I have the following expression:
15-JUL-16,20-JUL-16,20-JUL-16,30-JUL-16 in one of my columns.
I successfully used SUBSTR(REGEXP_SUBSTR(base.systemdate, '.+,'), 1, 9) to get 15-JUL-16 (expression until first comma) from the expression.
But I can't figure out how to get 30-JUL-16 (the last expression after last comma).
Is there some way to use REGEXP_SUBSTR to get that? And since we are at it.
Is there a neat way to only use REGEXP_SUBSTR to get 15-JUL-16 without comma? Because I am using second SUBSTR to get rid of the comma, so I can get it compatible with data format.
You can use a very similar construct:
SELECT REGEXP_SUBSTR(base.systemdate, '[^,]+$')
Oracle (and regular expressions in general) are "greedy". This means that they take the longest string. If you know the items in the list are all the same length, you could just use:
SELECT SUBSTR( ase.systemdate, -9)
Try this
select dates from
(
SELECT dates,max(id) over (partition by null) lastrec,min(id) over (partition by null) firstrec,id FROM (
with mine as(select '15-JUL-16,20-JUL-16,20-JUL-16,30-JUL-16' hello from dual)
select rownum id,regexp_substr(hello, '[^,]+', 1, level) dates from mine
connect by regexp_substr(hello, '[^,]+', 1, level) is not null)
)
where id=firstrec or id=lastrec
this query give you first and last record from comma separated list.