The row I am trying to parse from is a series of string values separated only by spaces. Sample below:
TX:123 SP:XapZNsyeS INST:456123
I need to use either regexp_substr or regexp_extract to return only values for the string that appears after "TX:" or "SP:", etc. So essentially an expression that only captures the string after a string (e.g. "TX:") and before a space (" ").
Here's one way to split on 2 delimiters. This works on Oracle 12c as you included the Oracle regexp-substr tag. Using a with statement, first set up the original data, then split on a space or the end of the line, then break into name-value pairs.
WITH tbl_original_data(ID, str) AS (
SELECT 1, 'TX:123 SP:XapZNsyeS INST:456123' FROM dual UNION ALL
SELECT 2, 'MI:321 SP:MfeKLgkrJ INST:654321' FROM dual
),
tbl_split_on_space(ID, ELEMENT) AS (
SELECT ID,
REGEXP_SUBSTR(str, '(.*?)( |$)', 1, LEVEL, NULL, 1)
FROM tbl_original_data
CONNECT BY REGEXP_SUBSTR(str, '(.*?)( |$)', 1, LEVEL) IS NOT NULL
AND PRIOR ID = ID
AND PRIOR SYS_GUID() IS NOT NULL
)
--SELECT * FROM tbl_split_on_space;
SELECT ID,
REGEXP_REPLACE(ELEMENT, '^(.*):.*', '\1') NAME,
REGEXP_REPLACE(ELEMENT, '.*:(.*)$', '\1') VALUE
FROM tbl_split_on_space;
ID NAME VALUE
---------- ---------- ----------
1 TX 123
1 SP XapZNsyeS
1 INST 456123
2 MI 321
2 SP MfeKLgkrJ
2 INST 654321
6 rows selected.
EDIT: Realizing this answer is a little more than was asked for, here's a simplified answer to return one element. Don't forget to allow for the ending of a space or the end of the line as well, in case you element is at the end of the line.
WITH tbl_original_data(ID, str) AS (
SELECT 1, 'TX:123 SP:XapZNsyeS INST:456123' FROM dual
)
SELECT REGEXP_SUBSTR(str, '.*?TX:(.*)( |$)', 1, 1, NULL, 1) TX_VALUE
FROM tbl_original_data;
TX_VALUE
--------
123
1 row selected.
Hello people here again with another oracle SQL question.
Im having some problems spliting values from a column to another one.
So there it goes.. im having this query :
SELECT MONEDA ,
LISTAGG (MONTO , ';') WITHIN GROUP (ORDER BY MONTO) MONTO,
REGEXP_SUBSTR(MONTO, '[^;]+', 1, 1) col_one,
REGEXP_SUBSTR(MONTO, '[^;]+', 1, 2) col_two
FROM (SELECT SUM(ZMT.AMOUNT) AS MONTO,
ZMT.T_TYPE AS tipo,
JSON_VALUE(MSG, '$.glAccount.currency.code') AS moneda
FROM Z_MAMBU_TRANSACTIONS ZMT JOIN POSTING_ONLINE0182 PO ON PO.RESP_REFERENCE0182 = ZMT.TRANSACTIONID
WHERE TO_CHAR(ZMT.CREATIONDATE, 'YYYY-MM-DD') = '2021-04-20' AND
PO.POSTING_RESPCODE0182 = 0 AND
(JSON_VALUE(MSG, '$.type') = 'DEBIT') OR (JSON_VALUE(MSG, '$.type') = 'CREDIT')
GROUP BY T_TYPE, JSON_VALUE(MSG, '$.glAccount.currency.code')
ORDER BY T_TYPE)
GROUP BY MONEDA
the result is the next:
https://i.stack.imgur.com/QMgYr.png
What i need to do is SPLIT the "MONTO" values with the ";" as separator to other 2 columns (col_one and col_two). As you can see in the result he is spliting me only the second value not the first.
After that i need to make the substract from the values that i split.
This is an exaple of what i need :
MONEDA MONTO COL_ONE COL_TWOV
COL 174579148065,39;175491229711,9 174579148065,39 175491229711,9
DOL 30300300300;30300300300 30300300300 30300300300
THANK YOU GUYS!
I agree with Tim - substr + instr do the job just nicely. If you, for some reason, want to try regular expressions, see if this helps (sample data in lines #1 - 4; query begins at line #5):
SQL> with result (moneda, monto) as
2 (select 'COL', '174579148065,39;175491229711,9' from dual union all
3 select 'DOL', '30300300300;30300300300' from dual
4 )
5 select moneda,
6 regexp_substr(monto, '\d+(,\d+)?', 1, 1) col_one,
7 regexp_substr(monto, '\d+(,\d+)?', 1, 2) col_two
8 from result;
MONEDA COL_ONE COL_TWO
---------- -------------------- --------------------
COL 174579148065,39 175491229711,9
DOL 30300300300 30300300300
SQL>
I would just use the base string functions here and avoid regex altogether. Going by your sample data given at the very end of your question:
SELECT
MONEDA,
MONTO,
SUBSTR(MONTO, 1, INSTR(MONTO, ';') - 1) AS COL_ONE,
SUBSTR(MONTO, INSTR(MONTO, ';') + 1, LENGTH(MONTO) - INSTR(MONTO, ';')) AS COL_TWO
FROM yourTable;
Demo
I have a Table - Product In Oracle, wherein p_spc_cat_id is stored as comma separated values.
p_id p_name p_desc p_spc_cat_id
1 AA AAAA 26,119,27,15,18
2 BB BBBB 0,0,27,56,57,4
3 BB CCCC 26,0,0,15,3,8
4 CC DDDD 26,0,27,7,14,10
5 CC EEEE 26,119,0,48,75
Now I want to search p_name which have p_spc_cat_id in '26,119,7' And this search value are not fixed it will some time '7,27,8'. The search text combination change every time
my query is:
select p_id,p_name from product where p_spc_cat_id in('26,119,7');
when i execute this query that time i can't find any result
I am little late in answering however i hope that i understood the question correctly.
Read further if: you have a table storing records like
1. 10,20,30,40
2. 50,40,20,70
3. 80,60,30,40
And a search string like '10,60', in which cases it should return rows 1 & 3.
Please try below, it worked for my small table & data.
create table Temp_Table_Name (some_id number(6), Ab varchar2(100))
insert into Temp_Table_Name values (1,'112,120')
insert into Temp_Table_Name values (2,'7,8,100,26')
Firstly lets breakdown the logic:
The table contains comma separated data in one of the columns[Column AB].
We have a comma separated string which we need to search individually in that string column. ['26,119,7,18'-X_STRING]
ID column is primary key in the table.
1.) Lets multiple each record in the table x times where x is the count of comma separated values in the search string [X_STRING]. We can use below query to create the cartesian join sub-query table.
Select Rownum Sequencer,'26,119,7,18' X_STRING
from dual
CONNECT BY ROWNUM <= (LENGTH( '26,119,7,18') - LENGTH(REPLACE( '26,119,7,18',',',''))) + 1
Small note: Calculating count of comma separated values =
Length of string - length of string without ',' + 1 [add one for last value]
2.) Create a function PARSING_STRING such that PARSING_STRING(string,position). So If i pass:
PARSING_STRING('26,119,7,18',3) it should return 7.
CREATE OR REPLACE Function PARSING_STRING
(String_Inside IN Varchar2, Position_No IN Number)
Return Varchar2 Is
OurEnd Number; Beginn Number;
Begin
If Position_No < 1 Then
Return Null;
End If;
OurEnd := Instr(String_Inside, ',', 1, Position_No);
If OurEnd = 0 Then
OurEnd := Length(String_Inside) + 1;
End If;
If Position_No = 1 Then
Beginn := 1;
Else
Beginn := Instr(String_Inside, ',', 1, Position_No-1) + 1;
End If;
Return Substr(String_Inside, Beginn, OurEnd-Beginn);
End;
/
3.) Main query, with the join to multiply records.:
select t1.*,PARSING_STRING(X_STRING,Sequencer)
from Temp_Table_Name t1,
(Select Rownum Sequencer,'26,119,7,18' X_STRING from dual
CONNECT BY ROWNUM <= (Select (LENGTH( '26,119,7,18') - LENGTH(REPLACE(
'26,119,7,18',',',''))) + 1 from dual)) t2
Please note that with each multiplied record we are getting 1 particular position value from the comma separated string.
4.) Finalizing the where condition:
Where
/* For when the value is in the middle of the strint [,value,] */
AB like '%,'||PARSING_STRING(X_STRING,Sequencer)||',%'
OR
/* For when the value is in the start of the string [value,]
parsing the first position comma separated value to match*/
PARSING_STRING(AB,1) = PARSING_STRING(X_STRING,Sequencer)
OR
/* For when the value is in the end of the string [,value]
parsing the last position comma separated value to match*/
PARSING_STRING(AB,(LENGTH(AB) - LENGTH(REPLACE(AB,',',''))) + 1) =
PARSING_STRING(X_STRING,Sequencer)
5.) Using distinct in the query to get unique ID's
[Final Query:Combination of all logic stated above: 1 Query to find them all]
select distinct Some_ID
from Temp_Table_Name t1,
(Select Rownum Sequencer,'26,119,7,18' X_STRING from dual
CONNECT BY ROWNUM <= (Select (LENGTH( '26,119,7,18') - LENGTH(REPLACE( '26,119,7,18',',',''))) + 1 from dual)) t2
Where
AB like '%,'||PARSING_STRING(X_STRING,Sequencer)||',%'
OR
PARSING_STRING(AB,1) = PARSING_STRING(X_STRING,Sequencer)
OR
PARSING_STRING(AB,(LENGTH(AB) - LENGTH(REPLACE(AB,',',''))) + 1) = PARSING_STRING(X_STRING,Sequencer)
You can use like to find it:
select p_id,p_name from product where p_spc_cat_id like '%26,119%'
or p_spc_cat_id like '%119,26%' or p_spc_cat_id like '%119,%,26%' or p_spc_cat_id like '%26,%,119%';
Use the Oracle function instr() to achieve what you want. In your case that would be:
SELECT p_name
FROM product
WHERE instr(p_spc_cat_id, '26,119') <> 0;
Oracle Doc for INSTR
If the string which you are searching will always have 3 values (i.e. 2 commas present) then you can use below approach.
where p_spc_cat_id like regexp_substr('your_search_string, '[^,]+', 1, 1)
or p_spc_cat_id like regexp_substr('your_search_string', '[^,]+', 1, 2)
or p_spc_cat_id like regexp_substr('your_search_string', '[^,]+', 1, 3)
If you cant predict how many values will be there in your search string
(rather how many commas) in that case you may need to generate dynamic query.
Unfortunately sql fiddle is not working currently so could not test this code.
SELECT p_id,p_name
FROM product
WHERE p_spc_cat_id
LIKE '%'||'&i_str'||'%'`
where i_str is 26,119,7 or 7,27,8
This solution uses CTE's. "product" builds the main table. "product_split" turns products into rows so each element in p_spc_cat_id is in it's own row. Lastly, product_split is searched for each value in the string '26,119,7' which is turned into rows by the connect by.
with product(p_id, p_name, p_desc, p_spc_cat_id) as (
select 1, 'AA', 'AAAA', '26,119,27,15,18' from dual union all
select 2, 'BB', 'BBBB', '0,0,27,56,57,4' from dual union all
select 3, 'BB', 'CCCC', '26,0,0,15,3,8' from dual union all
select 4, 'CC', 'DDDD', '26,0,27,7,14,10' from dual union all
select 5, 'CC', 'EEEE', '26,119,0,48,75' from dual
),
product_split(p_id, p_name, p_spc_cat_id) as (
select p_id, p_name,
regexp_substr(p_spc_cat_id, '(.*?)(,|$)', 1, level, NULL, 1)
from product
connect by level <= regexp_count(p_spc_cat_id, ',')+1
and prior p_id = p_id
and prior sys_guid() is not null
)
-- select * from product_split;
select distinct p_id, p_name
from product_split
where p_spc_cat_id in(
select regexp_substr('26,119,7', '(.*?)(,|$)', 1, level, NULL, 1) from dual
connect by level <= regexp_count('26,119,7', ',') + 1
)
order by p_id;
P_ID P_
---------- --
1 AA
3 BB
4 CC
5 CC
SQL>
I have a string with groups of nubmers. And Id like to make constant length string. Now I use two regexp_replace. First to add 10 numbers to string and next to cut string and take last 10 values:
with s(txt) as ( select '1030123:12031:1341' from dual)
select regexp_replace(
regexp_replace(txt, '(\d+)','0000000000\1')
,'\d+(\d{10})','\1') from s ;
But Id like to use only one regex something like
regexp_replace(txt, '(\d+)',lpad('\1',10,'0'))
But it don't work. lpad executed before regexp. Could you have any ideas?
With a slightly different approach, you can try the following:
with s(id, txt) as
(
select rownum, txt
from (
select '1030123:12031:1341' as txt from dual union all
select '1234:0123456789:1341' from dual
)
)
SELECT listagg(lpad(regexp_substr(s.txt, '[^:]+', 1, lines.column_value), 10, '0'), ':') within group (order by column_value) txt
FROM s,
TABLE (CAST (MULTISET
(SELECT LEVEL FROM dual CONNECT BY instr(s.txt, ':', 1, LEVEL - 1) > 0
) AS sys.odciNumberList )) lines
group by id
TXT
-----------------------------------
0001030123:0000012031:0000001341
0000001234:0123456789:0000001341
This uses the CONNECT BY to split every string based on the separator ':', then uses LPAD to pad to 10 and then aggregates the strings to build rows containing the concatenation of padded values
This works for non-empty sequences (e.g. 123::456)
with s(txt) as ( select '1030123:12031:1341' from dual)
select regexp_replace (regexp_replace (txt,'(\d+)',lpad('0',10,'0') || '\1'),'0*(\d{10})','\1')
from s
;
Suppose I have string
Str = 'Aaa,Bbb,Abb,Ccc'
I want to separate the above str in two parts as follows
Str1 = 'Aaa,Abb'
Str2 = 'Bbb,Ccc'
That is any word in str starting with A should go in str1 rest all in str2.
How can I achieve this using Oracle queries?
That is any word in str starting with A should go in str1 rest all in str2.
To achieve it in pure SQL, I will use the following:
REGEXP_SUBSTR
LISTAGG
SUBSTR
INLINE VIEW
So, first I will split the comma delimited string using the techniques as demonstrated here Split single comma delimited string into rows.
And then, I will aggregate them using LISTAGG in an order.
For example,
SQL> WITH
2 t1 AS (
3 SELECT 'Aaa,Bbb,Abb,Ccc' str FROM dual
4 ),
5 t2 AS (
6 SELECT trim(regexp_substr(str, '[^,]+', 1, LEVEL)) str
7 FROM t1
8 CONNECT BY LEVEL <= regexp_count(str, ',')+1
9 ORDER BY str
10 )
11 SELECT
12 (SELECT listagg(str, ',') WITHIN GROUP(
13 ORDER BY NULL) str1
14 FROM t2
15 WHERE SUBSTR(str, 1, 1)='A'
16 ) str1,
17 (SELECT listagg(str, ',') WITHIN GROUP(
18 ORDER BY NULL) str
19 FROM t2
20 WHERE SUBSTR(str, 1, 1)<>'A'
21 ) str2
22 FROM dual
23 /
STR1 STR2
---------- ----------
Aaa,Abb Bbb,Ccc
SQL>
The WITH clause is just for demonstration purpose, in your real scenario, remove the with clause and use you table name directly. Though it looks neat using the WITH clause.
Use regext expression and ListAg function.
NOTE: LISTAGG function is available since Oracle 11g!
select listagg(s.name, ',') within group (order by name)
from (select regexp_substr('Aaa,Bbb,Abb,Ccc,Add,Ddd','[^,]+', 1, level) name from dual
connect by regexp_substr('Aaa,Bbb,Abb,Ccc,Add,Ddd', '[^,]+', 1, level) is not null) s
group by decode(substr(name,1,1),'A', 1, 0);
This query gives you the desired output in two different rows:
with temp as (select trim (both ',' from 'Aaa,Bbb,Abb,Ccc') as str from dual),
base_table as
( select trim (regexp_substr (t.str,
'[^' || ',' || ']+',
1,
level))
str
from temp t
connect by instr (str,
',',
1,
level - 1) > 0),
ult_table as
(select str,
case upper (substr (str, 1, 1)) when 'A' then 1 else 2 end
as l
from base_table)
select listagg (case when l = 1 then str else null end, ',')
within group (order by str)
str1,
listagg (case when l = 2 then str else null end, ',')
within group (order by str)
str2
from ult_table;
Output
L STR
---------- --------------------------------------------------------------------------------
1 Aaa,Abb
2 Bbb,Ccc