Set flag column based on a regular expression - sql

I have developed the following query but it does not work as expected:
WITH TABLE1 AS
(
SELECT 613414473 as ID, 1706014200964 as P_NUM, 119539 as d_id, 'F20.0' AS CODE FROM DUAL UNION ALL
SELECT 613414473 as ID, 1706014200964 as P_NUM, 119539 as d_id, 'F22.0' AS CODE FROM DUAL UNION ALL
SELECT 613415801 as ID, 1707045167741 as P_NUM, 115182 as d_id, 'A94.0' AS CODE FROM DUAL UNION ALL
SELECT 613415801 as ID, 1707045167741 as P_NUM, 115182 as d_id, NULL AS CODE FROM DUAL UNION ALL
SELECT 613417084 as ID, 1702038456441 as P_NUM, 6541 as d_id, 'E79' AS CODE FROM DUAL UNION ALL
SELECT 613417084 as ID, 1702038456421 as P_NUM, 6541 as d_id, 'I10' AS CODE FROM DUAL UNION ALL
SELECT 613418372 as ID, 1706226211517 as P_NUM, 25727 as d_id, 'F32.9' AS CODE FROM DUAL )
SELECT T1.*
, CASE when regexp_like( CODE, 'C0[5-9]|' ||
'A0[0-9]|A1[0-9]|A2[0-9]|A3[0-9]|A4[0-9]|A5[0-9]|A6[0-9]|A7[0-9]|A8[0-9]|A9[0-7]|' )
THEN 1
ELSE 0 END AS FOUND_CODE
FROM TABLE1 T1;
I want codes which are like C0[5-9]% or A0[0-97] to be flagged with value 1, and then for the same p_num, if at least one code was found to set all the flags for that p_num to 1.
Example output of the above:
| 613414473|1706014200964|119539|F20.0|0|
| 613414473|1706014200964|119539|F22.0|0|
| 613415801|1707045167741|115182|A94.0|1|
| 613415801|1707045167741|115182|NULL |1|
| 613417084|1702038456441|6541 |E79 |0|
| 613417084|1702038456421|6541 |I10 |0|
| 613418372|1706226211517|25727 |F32.9|0|
How can I modify my query to get that output? And is there a better regular expression I can use?

Based on your description, the regular expression pattern should be
'^(C0[5-9]|A[0-8][0-9]|A9[0-7])'
The ^ anchors to the start of the value, and the parentheses allow any of the pipe-separated patterns to match; and the patterns are simplified, as A00 to A89 can be handles in one go.
That flags the same single row as your original query. The next stage is to move that into a subquery, and then use an analytic function partitioned by the p_num which you want to be common:
max(found_code) over (partition by p_num)
So together that becomes (with additional rows to match a different rule):
with table1 (id, p_num, d_id, code) as
(
select 613414470, 1706014200960, 119530, 'D99' from dual union all
select 613414471, 1706014200960, 119531, 'C05' from dual union all
--
select 613414473, 1706014200964, 119539, 'F20.0' from dual union all
select 613414473, 1706014200964, 119539, 'F22.0' from dual union all
select 613415801, 1707045167741, 115182, 'A94.0' from dual union all
select 613415801, 1707045167741, 115182, null from dual union all
select 613417084, 1702038456441, 6541 , 'E79' from dual union all
select 613417084, 1702038456421, 6541 , 'I10' from dual union all
select 613418372, 1706226211517, 25727 , 'F32.9' from dual
)
select id, p_num, d_id, code, max(found_code) over (partition by p_num) as found_code
from (
select t1.*
, case when regexp_like( code, '^(C0[5-9]|A[0-8][0-9]|A9[0-7])' )
then 1
else 0
end as found_code
from table1 t1
);
ID P_NUM D_ID CODE FOUND_CODE
------------- ------------- ------------- ----- -------------
613414470 1706014200960 119530 D99 1
613414471 1706014200960 119531 C05 1
613414473 1706014200964 119539 F20.0 0
613414473 1706014200964 119539 F22.0 0
613415801 1707045167741 115182 A94.0 1
613415801 1707045167741 115182 1
613417084 1702038456441 6541 E79 0
613417084 1702038456421 6541 I10 0
613418372 1706226211517 25727 F32.9 0

Related

Returning result in Json and table type SQL Oracle

I have a table in which with repeating id.
The goal is (to make a select) to get all the results of the repeating id to pour against each id in Json format as id it's not in Json
id,yyyy,name
1,2010,a
1,2011,b
2,2010,a
3,2010,c
3,2011,a
4,2011,v
Desired result
id, column(json)
1 {"2010":"a","2011","b"}
2 {"2010":"a","2010":"c"}
3 {"2010":"c","2011":"a"}
4 {"2011":"v"}
with data(id, y, name) as (
select 1,2010,'a' from dual union all
select 1,2011,'b' from dual union all
select 2,2010,'a' from dual union all
select 3,2010,'c' from dual union all
select 3,2011,'a' from dual union all
select 4,2011,'v' from dual -- union all
)
select id, json_objectagg(
key to_char(y) value name) as obj
from data
group by id
;
1 {"2010":"a","2011":"b"}
2 {"2010":"a"}
3 {"2010":"c","2011":"a"}
4 {"2011":"v"}

Convert a series of Number values in Text in Oracle SQL Query

In the Oracle database, I have string values (VARCHAR2) like 1,4,7,8. The number represents as 1=car, 2= bus, 3=BB, 4=SB, 5=Ba, 6=PA, 7=HB, and 8 =G
and want to convert the above-said example to "car,SB,HB,G" in my query results
I tried to use "Decode" but it does not work. Please advise how to make it works. Would appreciate.
Thanks`
Initially, I have used the following query:
Select Clientid as C#, vehicletypeExclusions as vehicle from
clients
The sample of outcomes are:
C# Vehicle
20 1,19,20,23,24,7,5
22 1,19,20,23,24,7,5
I also tried the following that gives me the null value of vehicles:
Select Clientid as C#, Decode (VEHICLETYPEEXCLUSIONS, '1', 'car',
'3','bus', '5','ba' ,'7','HB', '8','G'
, '9','LED1102', '10','LED1104', '13','LED8-2',
'14','Flip4-12', '17','StAT1003', '19','Taxi-Min', '20','Tax_Sed',
'21','Sup-veh' , '22','T-DATS', '23','T-Mini',
'24','T-WAM') as vehicle_Ex from clients >
Here's one option. Read comments within code. Sample data in lines #1 - 13; query begins at line #14.
SQL> with
2 expl (id, name) as
3 (select 1, 'car' from dual union all
4 select 2, 'bus' from dual union all
5 select 3, 'BB' from dual union all
6 select 4, 'SB' from dual union all
7 select 5, 'Ba' from dual union all
8 select 6, 'PA' from dual union all
9 select 7, 'HB' from dual union all
10 select 8, 'G' from dual
11 ),
12 temp (col) as
13 (select '1,4,7,8' from dual),
14 -- split COL to rows
15 spl as
16 (select regexp_substr(col, '[^,]+', 1, level) val,
17 level lvl
18 from temp
19 connect by level <= regexp_count(col, ',') + 1
20 )
21 -- join SPL with EXPL; aggregate the result
22 select listagg(e.name, ',') within group (order by s.lvl) result
23 from expl e join spl s on s.val = e.id;
RESULT
--------------------------------------------------------------------------------
car,SB,HB,G
SQL>
Using the function f_subst from https://stackoverflow.com/a/68537479/429100 :
create or replace
function f_subst(str varchar2, template varchar2, subst sys.odcivarchar2list) return varchar2
as
res varchar2(32767):=str;
begin
for i in 1..subst.count loop
res:=replace(res, replace(template,'%d',i), subst(i));
end loop;
return res;
end;
/
I've replaced ora_name_list_t (nested table) with sys.odcivarchar2list (varray) to make this example easier, but I would suggest to create your own collection for example create type varchar2_table as table of varchar2(4000);
Example:
select
f_subst(
'1,4,7,8'
,'%d'
,sys.odcivarchar2list('car','bus','BB','SB','Ba','PA','HB','G')
) s
from dual;
S
----------------------------------------
car,SB,HB,G
Assume you have a lookup table (associating the numeric codes with descriptions) and a table of input strings, which I called sample_inputs in my tests, as shown below:
create table lookup (code, descr) as
select 1, 'car' from dual union all
select 2, 'bus' from dual union all
select 3, 'BB' from dual union all
select 4, 'SB' from dual union all
select 5, 'Ba' from dual union all
select 6, 'PA' from dual union all
select 7, 'HB' from dual union all
select 8, 'G' from dual
;
create table sample_inputs (str) as
select '1,4,7,8' from dual union all
select null from dual union all
select '3' from dual union all
select '5,5,5' from dual union all
select '6,2,8' from dual
;
One strategy for solving your problem is to split the input - slightly modified to make it a JSON array, so that we can use json_table to split it - then join to the lookup table and re-aggregate.
select s.str, l.descr_list
from sample_inputs s cross join lateral
( select listagg(descr, ',') within group (order by ord) as descr_list
from json_table( '[' || str || ']', '$[*]'
columns code number path '$', ord for ordinality)
join lookup l using (code)
) l
;
STR DESCR_LIST
------- ------------------------------
1,4,7,8 car,SB,HB,G
3 BB
5,5,5 Ba,Ba,Ba
6,2,8 PA,bus,G

Is there a function to check if the given string pattern in valid

I need an Oracle function to check if the given input string is valid.
For eg. input string is 50-100,145,153
The function should check
num1 < num2 in num1-num2
the string pattern should always be num1-num2,num3,num4
Thanks
There's no built-in function, but you can use regular expressions. For example (only the first row is valid):
SQL> with test (col) as
2 (select '50-100,145,153' from dual union all
3 select '50-49,145,153' from dual union all
4 select 'a-2,b,200' from dual union all
5 select '10-20,30' from dual union all
6 select '1,2-3,4' from dual
7 )
8 select col
9 from test
10 where regexp_like(col, '^\d+-\d+,\d+,\d+$')
11 and to_number(regexp_substr(col, '\w+', 1, 1)) < to_number(regexp_substr(col, '\w+', 1, 2));
COL
--------------
50-100,145,153
SQL>
I would use this approach:
with test (col) as
(select '50-100,145,153' from dual union all -- ok
select '50-49,145,153' from dual union all
select 'a-2,b,200' from dual union all
select '10-20,30' from dual union all -- ok
select '1,2-3,4' from dual union all -- ok
-- additional test values:
select '1,2,3,7-8,100,120-130,200' from dual union all -- ok
select '1-3,7-8,120-130,200' from dual union all -- ok
select '1,2,3,8-7,100,120-130,200' from dual union all -- nope
select '1,2,3,7-8,100,130-120,200' from dual union all -- nope
select '1,2,3,7-8,100,,,120-z,200' from dual union all -- nope
select '1,2,3,7-8,100,,,120,200' from dual -- nope
)
select col
from test
where regexp_like(col, '^(((\d+-\d+)|\d+),?)+$')
and (select
count(*)
from dual
where to_number(regexp_substr(col,'(\d+)-(\d+)',1,level,null,1))
> to_number(regexp_substr(col,'(\d+)-(\d+)',1,level,null,2))
connect by level<=regexp_count(col,'(\d+)-(\d+)')
) = 0;
regexp_like(col, '^(((\d+-\d+)|\d+),?)+$') checks that col consist only from 2 patterns: \d+-\d+ and \d+ with comma between them.
in the subquery we check if there is num1>num2

SELECT STATMENT IN OR LIKE OR REGEXP

I have a table new_table
ID DESCRIPTION
1 abdad jadjnd kandkadn 01/19-P-154-37
2 jscbsjc jscnscj 01/19-H-443-38 sbjcj sjcnjscn
3 scjbcs sc, scnsc 01/19-P-16-39 sjcbnjcs
4 scbcsjc 01/19-K-139-40 hcbchsb
AND LISTS
01/19-P-154-37
01/19-H-443-38
01/19-K-139-40
I want to
select * from new_table where descriptin in (
01/19-P-154-37
01/19-H-443-38
01/19-P-16-39
01/19-K-139-40
)
OR LIKE I don't now please help
I guess you want something like this (join with like operator):
with new_table (ID, DESCRIPTION) as (
select 1 ,'abdad jadjnd kandkadn 01/19-P-154-37' from dual union all
select 2 ,'jscbsjc jscnscj 01/19-H-443-38 sbjcj sjcnjscn' from dual union all
select 3 ,'scjbcs sc, scnsc 01/19-P-16-39 sjcbnjcs' from dual union all
select 4 ,'scbcsjc 01/19-K-139-40 hcbchsb' from dual
) ,
LISTS(val) as(
select '01/19-P-154-37' from dual union all
select '01/19-H-443-38' from dual union all
select '01/19-K-139-40' from dual
)
-- Below is actual query:
select new_table.* from new_table
inner join LISTS
on new_table.DESCRIPTION like '%'||LISTS.val||'%'

Find the top-level parent group each record belongs to

I have hierarchical data with the following structure. The levels are 1 to 6, so instead of doing 6 joins and then coalesce, how can I find what is the top-level parent (which has no parent_nr).
I've tried the accepted answer here,
SELECT
aa.code_nr,
aa.parent_nr,
CONNECT_BY_ROOT aa.code_nr AS "Top Level ID"
FROM mytable_t aa
CONNECT BY PRIOR aa.code_nr = aa.parent_nr
;
but it only gives me the next level as "Top Level ID" and not the final level (A)
Oracle Setup:
CREATE TABLE my_table ( code_nr, parent_nr ) AS (
SELECT 'A', NULL FROM DUAL UNION ALL
SELECT 'A.1', 'A' FROM DUAL UNION ALL
SELECT 'A.1.1', 'A.1' FROM DUAL UNION ALL
SELECT 'A.1.1.1', 'A.1.1' FROM DUAL UNION ALL
SELECT 'A.1.1.2', 'A.1.1' FROM DUAL UNION ALL
SELECT 'A.1.1.1.1', 'A.1.1.1' FROM DUAL UNION ALL
SELECT 'A.1.1.2.1', 'A.1.1.2' FROM DUAL UNION ALL
SELECT 'A.1.1.2.2', 'A.1.1.2' FROM DUAL;
Query:
SELECT LEVEL,
code_nr AS root_code_nr,
CONNECT_BY_ROOT( code_nr ) AS code_nr
FROM my_table
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR parent_nr = code_nr;
Output:
LEVEL ROOT_CODE CODE_NR
----- --------- ---------
1 A A
2 A A.1
3 A A.1.1
4 A A.1.1.1
5 A A.1.1.1.1
4 A A.1.1.2
5 A A.1.1.2.1
5 A A.1.1.2.2
You could try the following:
SELECT
aa.code_nr,
aa.parent_nr,
substr(SYS_CONNECT_BY_PATH(aa.code_nr, '/'),2,instr(SYS_CONNECT_BY_PATH(aa.code_nr, '/'),'/'))
FROM mytable_t aa
CONNECT BY PRIOR aa.code_nr = aa.parent_nr
;