Extract TLD from a domain name and group them based on TLD - sql

I need to extract TLD from the web address and if it matches with a pre-defined set of TLDs(com,edu,nz,au), I need to sum it up. In case it doesn't match with the pre-defined TLD, It should be categorized in the "Other" category. If the webadress isn't available for a particular bussiness, it should be categorized in the "Not available".
Expected Output:
CLIENT TYPE TOTAL
------------- ----------
com 4
au 5
nz 0
Not Available 0
Other 0
I have written the following query but it does not give me rows which have 0 value.
select tld2, NVL(cnt,0) from (select REGEXP_SUBSTR (webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL, 1) as tld2, count(*) cnt from client group by REGEXP_SUBSTR (webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL, 1))a where tld2 in ('com','edu','gov','org')
UNION ALL
select 'Not Available' as tld2, COUNT(webaddress) from client where webaddress is null
UNION
select 'Other' as tld2, NVL(cnt,0) from (select REGEXP_SUBSTR (webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL, 1) as tld2, count(*) cnt from client group by REGEXP_SUBSTR (webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL, 1))a where tld2 not in ('com','edu','gov','org');
Can someone please guide me if I should use cases here?

Please try A little edit of your approach:
select tld2, NVL(cnt,0) from (select REGEXP_SUBSTR (webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL, 1) as tld2, count(*) cnt from client group by REGEXP_SUBSTR (webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL, 1))a where tld2 in ('com','edu','gov','org')
UNION ALL
select 'Not Available' as tld2, cnt from (select COUNT(webaddress) cnt from client where webaddress is null)
UNION
select 'Other' as tld2, cnt from (select count(webaddress) cnt from client where REGEXP_SUBSTR (webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL, 1) not in ('com','edu','gov','org'))a ;
Thanks.

You can create a Java function to find the TLD (since your regular expression doesn't handle the case when there are port numbers, and possibly other edge cases such as https://localhost/not/at/example.com/, and using an API designed to handle URIs would be better):
CREATE AND COMPILE JAVA SOURCE NAMED URIHandler AS
import java.net.URI;
import java.net.URISyntaxException;
public class URIHandler {
public static String getTLD( final String url )
{
String domain = null;
try
{
URI uri = new URI( url );
domain = uri.getHost();
}
catch ( URISyntaxException ex )
{
}
if ( domain == null )
{
return null;
}
int index = domain.lastIndexOf( "." );
return ( index >= 0 ? domain.substring( index + 1 ) : domain );
}
}
/
Which you can then wrap in a PL/SQL function:
CREATE FUNCTION getTLD( url IN VARCHAR2 ) RETURN VARCHAR2
AS LANGUAGE JAVA NAME 'URIHandler.getTLD( java.lang.String ) return java.lang.String';
/
Then you can use the code:
WITH tlds ( tld ) AS (
SELECT 'Not Available' FROM DUAL UNION ALL
SELECT 'com' FROM DUAL UNION ALL
SELECT 'nz' FROM DUAL UNION ALL
SELECT 'au' FROM DUAL UNION ALL
SELECT 'Other' FROM DUAL
),
matches ( match ) AS (
SELECT DECODE(
getTLD( url ),
NULL, 'Not Available',
'com', 'com',
'au', 'au',
'nz', 'nz',
'Other'
)
FROM table_name
)
SELECT t.tld,
COUNT( m.match )
FROM tlds t
LEFT OUTER JOIN matches m
ON ( t.tld = m.match )
GROUP BY
t.tld;
Which, for the sample data:
CREATE TABLE table_name ( url ) AS
SELECT 'http://example.com' FROM DUAL UNION ALL
SELECT 'http://example.com:80/' FROM DUAL UNION ALL
SELECT 'https://example.au' FROM DUAL UNION ALL
SELECT 'https://example.au:442/' FROM DUAL UNION ALL
SELECT 'https://example.nz/not/at/example.com/' FROM DUAL UNION ALL
--SELECT 'https://example.net' FROM DUAL UNION ALL
SELECT 'not a URI' FROM DUAL;
Outputs:
TLD | COUNT(M.MATCH)
:------------ | -------------:
Other | 0
com | 2
nz | 1
au | 2
Not Available | 1
db<>fiddle here

take a look at this example:
with sub as (
select case when webaddress is null then 'Not Available'
when domain_name in ('com','edu','gov','org') then domain_name else 'Other' end client_type
from (
SELECT
regexp_substr(webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL,
1) domain_name,
webaddress
FROM
(
SELECT
'https://stackoverflow.com/questions/65096217/' webaddress
FROM
dual
UNION ALL
SELECT
'https://stackoverflow.edu/questions/65096217/'
FROM
dual
UNION ALL
SELECT
'https://stackoverflow.edu/questions/6509621/'
FROM
dual
UNION ALL
select 'https://stackoverflow.de/questions/65096217/'
from dual
/*UNION ALL
select null
from dual*/
))),
cat as (select regexp_substr('Not Available,com,edu,gov,org,Other','[^,]+', 1, level ) val
from dual
connect by regexp_substr('Not Available,com,edu,gov,org,Other', '[^,]+', 1, level) is not null)
select c.val, sum(case when s.client_type is null then 0 else 1 end)
from sub s right outer join cat c on (c.val = s.client_type)
group by c.val;
previous (incomplete sol):
very simple solution using plain group by and case stmt. could be this:
select case when s.webaddress is null then 'Not Available'
when s.domain_name in ('com','edu','gov','org') then s.domain_name else 'Other' end client_type,
count(*)
from (SELECT
regexp_substr(webaddress, '\.([a-z]+)(/|$)', 1, 1, NULL,
1) domain_name,
webaddress
FROM
(
SELECT
'https://stackoverflow.com/questions/65096217/' webaddress
FROM
dual
UNION ALL
SELECT
'https://stackoverflow.edu/questions/65096217/'
FROM
dual
UNION ALL
SELECT
'https://stackoverflow.edu/questions/6509621/'
FROM
dual
UNION ALL
SELECT
NULL
FROM
dual
UNION ALL
select 'https://stackoverflow.de/questions/65096217/'
from dual
)) s
group by case when s.webaddress is null then 'Not Available'
when s.domain_name in ('com','edu','gov','org') then s.domain_name else 'Other' end
;

Related

Replace some variables by data of another table in sql oracle

I have a table with two columns
type
TXT
A
this is some text for %1 and %2
B
this is another step for %1
in a translation table I have the signification of the variables %X that looks like
Type
variable
descr
A
%1
#person1#
A
%2
#person2#
B
%1
#manager#
I want to replace in my first table all the variables by the description, so the result has to looks like this:
type
TXT
A
this is some text for #person1# and #person2#
B
this is another step for #manager#
I tried with a replace, but I didn't figured out how to make it work
To replace all variables you could use a recursive algorithm:
with data(typ, txt) as (
select 'A', 'this is some text for %1 and %2' from dual union all
select 'B', 'this is another step for %1' from dual
),
translations(typ, var, description) as (
select 'A', '%1', '#person1#' from dual union all
select 'A', '%2', '#person2#' from dual union all
select 'B', '%1', '#manager#' from dual -- union all
),
rtranslations(typ, var, description,rn) as (
select t.*, row_number() over(partition by typ order by var) as rn
from translations t
),
replacecte(typ, txt, replaced_txt, rn) as (
select d.typ, d.txt, replace(d.txt, t.var, t.description), t.rn
from data d
join rtranslations t on t.typ = d.typ
where t.rn = 1
union all
select r.typ, r.txt, replace(r.replaced_txt, t.var, t.description), t.rn
from replacecte r
join rtranslations t on t.typ = r.typ and t.rn = r.rn + 1
)
select r.typ, r.txt, replaced_txt from replacecte r
where rn = length(txt) - length(replace(txt,'%',''))
;
You can also do it this way without recursion. data and descr are of course just mock ups for your tables, you would not need any WITH clauses. This method uses the steps (1) break up the sentences into words, (2) outer join using those words to your description table, replacing any matches with the description values, (3) reassemble the words back into sentences using LISTAGG.
WITH data AS(SELECT 'A' type, 'this is some text for %1 and %2' txt FROM dual
UNION ALL
SELECT 'B' type, 'this is another step for %1' txt FROM dual
),
descr AS (SELECT 'A' type, '%1' variable,'#person1#' description FROM dual
UNION ALL
SELECT 'A' type, '%2' variable,'#person2#' description FROM dual
UNION ALL
SELECT 'B' type, '%1' variable,'#manager#' description FROM dual)
SELECT type,
LISTAGG(new_word,' ') WITHIN GROUP (ORDER BY seq) txt
FROM (SELECT x.type,
NVL(descr.description,x.word) new_word,
seq
FROM (SELECT type,SUBSTR(' '||txt,INSTR(' '||txt,' ',1,seq)+1,INSTR(' '||txt||' ',' ',1,seq+1) - (INSTR(' '||txt,' ',1,seq)+1)) word,seq
FROM data,
(SELECT ROWNUM seq FROM dual CONNECT BY LEVEL <= 50) x) x,
descr
WHERE x.type = descr.type(+)
AND x.word = descr.variable(+))
GROUP BY type
You could use PIVOT to get the var values from rows into columns (geting all vars in the same row with text) and then do multiple replaces depending on number of var values:
SELECT t.A_TYPE,
CASE WHEN d.V3 Is Not Null THEN REPLACE(REPLACE(REPLACE(t.TXT, '%1', d.V1), '%2', d.V2), '%3', d.V3)
WHEN d.V2 Is Not Null THEN REPLACE(REPLACE(t.TXT, '%1', d.V1), '%2', d.V2)
WHEN d.V1 Is Not Null THEN REPLACE(t.TXT, '%1', d.V1)
ELSE t.TXT
END "TXT"
FROM tbl t
INNER JOIN ( SELECT *
FROM ( Select A_TYPE, VAR, DESCRIPTION FROM descr )
PIVOT ( MAX(DESCRIPTION) For VAR IN('%1' "V1", '%2' "V2", '%' "V3") )
) d ON(d.A_TYPE = t.A_TYPE)
With sample data as:
WITH
tbl (A_TYPE, TXT) AS
(
Select 'A', 'this is some text for %1 and %2' From Dual Union All
Select 'B', 'this is another step for %1' From dual
),
descr (A_TYPE, VAR, DESCRIPTION) AS
(
Select 'A', '%1', '#person1#' From Dual UNION ALL
Select 'A', '%2', '#person2#' From Dual UNION ALL
Select 'B', '%1', '#manager#' From Dual
)
... the result should be
A_TYPE TXT
------ -----------------------------------------------
A this is some text for #person1# and #person2#
B this is another step for #manager#

Update based on the comma seperated value

I have the below Table with two columns both columns are VARCHAR2(100).
PARAM_NAME PARAM_VALUE
PlanName,DemandMonth EUMOCP,01-2022
PlanName,DemandMonth EUMOCP,02-2022
PlanName,DemandMonth EUMOCP,03-2022
PlanName,DemandMonth EUMOCP,04-2021
How can we write a update on the table so that it only updates the corresponding value.
For example:
Update DemandMonth from 01-2022 to 04-2022.
Provided it only updates the columns based on the first column
For instance,
Column A Column B
1,2 3,4
based on 1 we can update 3 as it is before ',' similarly based on 2 we can update 4.
What we want to achieve is the first it identifies where is 'DemandMonth' and then accordingly update the second column. Also if possible can we write it for 4 or 5 comma seperated values?
Don't store values in delimited strings.
Change your table so the values are:
CREATE TABLE params ( id, param_name, param_value ) AS
SELECT 1, 'PlanName', 'EUMOCP' FROM DUAL UNION ALL
SELECT 1, 'DemandMonth', '01-2022' FROM DUAL UNION ALL
SELECT 2, 'PlanName', 'EUMOCP' FROM DUAL UNION ALL
SELECT 2, 'DemandMonth', '02-2022' FROM DUAL UNION ALL
SELECT 3, 'PlanName', 'EUMOCP' FROM DUAL UNION ALL
SELECT 3, 'DemandMonth', '03-2022' FROM DUAL UNION ALL
SELECT 4, 'PlanName', 'EUMOCP' FROM DUAL UNION ALL
SELECT 4, 'DemandMonth', '04-2021' FROM DUAL;
Then all you need to do to update the value is:
UPDATE params
SET param_value = '04-2022'
WHERE param_name = 'DemandMonth'
AND param_value = '01-2022';
There is no worrying about where in the delimited string the value is and it is all simple.
You should not do this and should refactor your table to not use delimited strings... however, you can use:
MERGE INTO params dst
USING (
WITH items ( rid, param_names, param_values, name, value, lvl, max_lvl ) AS (
SELECT ROWID,
param_name,
param_value,
REGEXP_SUBSTR( param_name, '[^,]+', 1, 1 ),
REGEXP_SUBSTR( param_value, '[^,]+', 1, 1 ),
1,
REGEXP_COUNT( param_value, '[^,]+' )
FROM params
UNION ALL
SELECT rid,
param_names,
param_values,
REGEXP_SUBSTR( param_names, '[^,]+', 1, lvl + 1 ),
REGEXP_SUBSTR( param_values, '[^,]+', 1, lvl + 1 ),
lvl + 1,
max_lvl
FROM items
WHERE lvl < max_lvl
)
SELECT rid,
LISTAGG(
CASE
WHEN name = 'DemandMonth' AND value = '01-2022'
THEN '04-2022'
ELSE value
END,
','
) WITHIN GROUP ( ORDER BY lvl ) AS param_value
FROM items
GROUP BY rid
HAVING COUNT(
CASE
WHEN name = 'DemandMonth' AND value = '01-2022'
THEN 1
END
) > 0
) src
ON ( dst.ROWID = src.rid )
WHEN MATCHED THEN
UPDATE SET param_value = src.param_value;
Which, for the sample data:
CREATE TABLE params ( param_name, param_value ) AS
SELECT 'PlanName,DemandMonth', 'EUMOCP,01-2022' FROM DUAL UNION ALL
SELECT 'PlanName,DemandMonth', 'EUMOCP,02-2022' FROM DUAL UNION ALL
SELECT 'PlanName,DemandMonth', 'EUMOCP,03-2022' FROM DUAL UNION ALL
SELECT 'PlanName,DemandMonth', 'EUMOCP,04-2021' FROM DUAL;
Then:
SELECT * FROM params;
Outputs:
PARAM_NAME
PARAM_VALUE
PlanName,DemandMonth
EUMOCP,04-2022
PlanName,DemandMonth
EUMOCP,02-2022
PlanName,DemandMonth
EUMOCP,03-2022
PlanName,DemandMonth
EUMOCP,04-2021
db<>fiddle here

Big query find data that could be in multiple columns

I have a table with the following data
id|task1_name|task1_date|task2_name|task2_date
1,breakfast,1/1/20,,
2,null,null,breakfast,,1/1/20
3,null,null,lunch,,1/1/20
4,dinner,1/1/20,lunch,1/1/10
I'd like to build a view that always displayed the task names in the same column or null if it could not be found in any of the columns e.g.
id|dinner_date|lunch_date|breakfast_date
1,1/1/20, null, null
2,null, null, 1/1/20
2,1/1/20, 1/1/10, null
I've tried using a nested IF statement e.g.
SELECT *
IF(task_1_name = 'dinner', task1_date, IF(task2_date = 'dinner', task2_date, NULL)) as `dinner_date`
FROM t
But as there are 50 or so columns in the real dataset, this seems like a stupid solution and would get complex very quickly, is there a smarter way here?
One method uses case expressions:
select t.*,
(case when task1_name = 'dinner' then task1_date
when task2_name = 'dinner' then task2_date
when task3_name = 'dinner' then task3_date
end) as dinner_date
from t;
Below is for BigQuery Standard SQL and generic enough to addresses concerns expressed in question. You don't need to know in advance number of columns and tasks names (although they should not have , or : which should not be a big limitation here and can be addressed if needed)
#standardSQL
CREATE TEMP TABLE ttt AS
SELECT id,
SPLIT(k, '_')[OFFSET(0)] task,
MAX(IF(SPLIT(k, '_')[OFFSET(1)] = 'name', v, NULL)) AS name,
MAX(IF(SPLIT(k, '_')[OFFSET(1)] = 'date', v, NULL)) AS DAY
FROM (
SELECT id,
TRIM(SPLIT(kv, ':')[OFFSET(0)], '"') k,
TRIM(SPLIT(kv, ':')[OFFSET(1)], '"') v
FROM `project.dataset.table` t,
UNNEST(SPLIT(TRIM(TO_JSON_STRING(t), '{}'))) kv
WHERE TRIM(SPLIT(kv, ':')[OFFSET(0)], '"') != 'id'
AND TRIM(SPLIT(kv, ':')[OFFSET(1)], '"') != 'null'
)
GROUP BY id, task;
EXECUTE IMMEDIATE '''
SELECT id, ''' || (
SELECT STRING_AGG(DISTINCT "MAX(IF(name = '" || name || "', day, NULL)) AS " || name || "_date")
FROM ttt
) || '''
FROM ttt
GROUP BY 1
ORDER BY 1
'''
Note; the assumption here is only about columns name to be task<N>_name and task<N>_date
If to apply to sample data (similar) to yours in question
WITH `project.dataset.table` AS (
SELECT 1 id, 'breakfast' task1_name, '1/1/21' task1_date, NULL task2_name, NULL task2_date UNION ALL
SELECT 2, NULL, NULL, 'breakfast', '1/1/22' UNION ALL
SELECT 3, NULL, NULL, 'lunch', '1/1/23' UNION ALL
SELECT 4, 'dinner', '1/1/24', 'lunch', '1/1/10'
)
output is
Row id breakfast_date lunch_date dinner_date
1 1 1/1/21 null null
2 2 1/1/22 null null
3 3 null 1/1/23 null
4 4 null 1/1/10 1/1/24
Here is another solution which doesn't use dynamic SQL, doesn't rely on specific column names and works with arbitrary number of columns:
WITH table AS (
SELECT 1 id, 'breakfast' task1_name, '1/1/21' task1_date, NULL task2_name, NULL task2_date UNION ALL
SELECT 2, NULL, NULL, 'breakfast', '1/1/22' UNION ALL
SELECT 3, NULL, NULL, 'lunch', '1/1/23' UNION ALL
SELECT 4, 'dinner', '1/1/24', 'lunch', '1/1/10'
)
SELECT
REGEXP_EXTRACT(f, r'breakfast\, ([^\,\)]*)'),
REGEXP_EXTRACT(f, r'lunch\, ([^\,\)]*)'),
REGEXP_EXTRACT(f, r'dinner\, ([^\,\)]*)')
FROM (
SELECT FORMAT("%t", t) f FROM table t
)

PL/SQL: Invalid Number error

I am creating a procedure for which I collect data by repeatedly running the following query.
SELECT ATTRIBUTE_VALUE,
COUNT(src1) CNT1,
COUNT(src2) CNT2
FROM (
SELECT a.ATTRIBUTE_VALUE,
1 src1,
TO_NUMBER(NULL) src2
FROM (
SELECT DECODE(
L,
1, IP_ADDRESS,
DECODE(
L,
2, IP_SUBNET_MASK,
DECODE(
L,
3, IP_DEFAULT_GATEWAY
)
)
) ATTRIBUTE_VALUE
FROM ( SELECT LEVEL L FROM DUAL X CONNECT BY LEVEL <= 3 ),
REUT_LOAD_IP_ADDRESSES
WHERE LIP_IPT_NAME = 'CE'
AND IP_LNT_ID IN (
SELECT LNT_ID
FROM REUT_LOAD_NTN
WHERE LNT_ID IN (
SELECT RLPN.LPN_LNT_ID
FROM REUT_LOAD_PI_NTN RLPN
WHERE LPN_LPI_ID IN (
SELECT RLPI.LPI_ID
FROM REUT_LOAD_PAC_INS RLPI
WHERE RLPI.LPI_DATE_ADDED IN (
SELECT MAX(RLPI2.LPI_DATE_ADDED)
FROM REUT_LOAD_PAC_INS RLPI2
WHERE RLPI2.PI_JOB_ID = P_ORDER_ID
)
)
)
AND IP_CEASE_DATE IS NULL
AND LNT_SERVICE_INSTANCE = 'PRIMARY'
)
It is running fine in SQL developer but when executing it as a procedure, I am getting INVALID NUMBER ERROR (ORA-01722: invalid number) at
AND IP_LNT_ID IN (
SELECT LNT_ID, in the code.
Can I get any help?
The error is pretty clear. You're comparing a number to another type of value.
Example:
SELECT 'x'
FROM DUAL
WHERE 1 IN (SELECT 'a'
FROM DUAL)
This means that IP_LNT_ID, LNT_ID, LPN_LNT_ID and LPI_ID have to be NUMBER. And LPI_DATE_ADDED and LPI_DATE_ADDED should both be date or timestamp.
If this is not possible you could compare everything as char:
SELECT ATTRIBUTE_VALUE, COUNT(src1) CNT1, COUNT(src2) CNT2
FROM (SELECT a.ATTRIBUTE_VALUE, 1 src1, TO_NUMBER(NULL) src2
FROM (SELECT
DECODE(L,1,IP_ADDRESS,DECODE(L,2,IP_SUBNET_MASK,DECODE(L,3,IP_DEFAULT_GATEWAY) ) ) ATTRIBUTE_VALUE
FROM
(
SELECT LEVEL L FROM DUAL X CONNECT BY LEVEL <= 3
),
REUT_LOAD_IP_ADDRESSES
WHERE LIP_IPT_NAME = 'CE'
AND to_char(IP_LNT_ID) IN (
SELECT LNT_ID
FROM REUT_LOAD_NTN
WHERE to_char(LNT_ID) IN (
SELECT RLPN.LPN_LNT_ID
FROM REUT_LOAD_PI_NTN RLPN
WHERE to_char(LPN_LPI_ID) IN (
SELECT RLPI.LPI_ID
FROM REUT_LOAD_PAC_INS RLPI
WHERE to_char(RLPI.LPI_DATE_ADDED) IN (
SELECT MAX(RLPI2.LPI_DATE_ADDED)
FROM REUT_LOAD_PAC_INS RLPI2
WHERE RLPI2.PI_JOB_ID = P_ORDER_ID
)
)
)
AND IP_CEASE_DATE IS NULL
AND LNT_SERVICE_INSTANCE = 'PRIMARY'
)
But this should be avoided on any cost. Unfortunately some times we have to cheat a little from time to time to work with our existing infrasructure ;-)
You need to make sure:
REUT_LOAD_IP_ADDRESSES.IP_LNT_ID
and
REUT_LOAD_NTN.LNT_ID
Have the same data type or cast/convert one or other so that they have the same data type.
There are multiple other issues:
You have aggregated and non-aggregated values:
SELECT ATTRIBUTE_VALUE,
COUNT(src1) CNT1,
COUNT(src2) CNT2
FROM ( ... )
Without a GROUP BY clause.
src2 is TO_NUMBER(NULL) which is just NULL and COUNT(NULL) will always be 0 so your query is:
SELECT ATTRIBUTE_VALUE,
COUNT(src1) CNT1,
0 CNT2
...
This code:
SELECT DECODE(
L,
1, IP_ADDRESS,
DECODE(
L,
2, IP_SUBNET_MASK,
DECODE(
L,
3, IP_DEFAULT_GATEWAY
)
)
) ATTRIBUTE_VALUE
FROM ( SELECT LEVEL L FROM DUAL X CONNECT BY LEVEL <= 3 ),
REUT_LOAD_IP_ADDRESSES
Can be rewritten as:
SELECT DECODE(
L,
1, IP_ADDRESS,
2, IP_SUBNET_MASK,
3, IP_DEFAULT_GATEWAY
) ATTRIBUTE_VALUE
FROM ( SELECT LEVEL L FROM DUAL X CONNECT BY LEVEL <= 3 ),
REUT_LOAD_IP_ADDRESSES
Or, without the join as:
SELECT attribute_value
FROM REUT_LOAD_IP_ADDRESSES
UNPIVOT ( attribute_value FOR L IN (
IP_ADDRESS AS 1,
IP_SUBNET_MASK AS 2,
IP_DEFAULT_GATEWAY AS 3
) )
The innermost query:
SELECT RLPI.LPI_ID
FROM REUT_LOAD_PAC_INS RLPI
WHERE RLPI.LPI_DATE_ADDED IN (
SELECT MAX(RLPI2.LPI_DATE_ADDED)
FROM REUT_LOAD_PAC_INS RLPI2
WHERE RLPI2.PI_JOB_ID = P_ORDER_ID
)
The inner query is restricted to have RLPI2.PI_JOB_ID = P_ORDER_ID but there is no correlation between the outer query so you can retrieve results that do not match P_ORDER_ID but just happen to have the same date as a matching row.

Find a substring in Oracle SQL “after first _ (underscore) to start” and “second _ (underscore) to end” using REGEXP_SUBSTR or SUBSTR function

My Input pattern like:
WITH data_tab AS (
SELECT '1540_INPUTTER' user_name FROM dual
UNION SELECT '1540_RAZZ25_UNKNOWN' FROM dual
UNION SELECT '1540_RAKIB17_OS_WIN10' FROM dual
)
SELECT REGEXP_SUBSTR(user_name,…………………….....) AS st_user_name from data_tab
Desired Output:
ST_USER_NAME
------------
INPUTTER
RAZZ25
RAKIB17
One way to do that is
WITH data_tab AS (
SELECT '1540_INPUTTER' user_name FROM dual
UNION SELECT '1540_RAZZ25_UNKNOWN' FROM dual
UNION SELECT '1540_RAKIB17_OS_WIN10' FROM dual
)
SELECT REGEXP_SUBSTR(user_name,'_([^_]*)', 1, 1, 'i', 1) AS st_user_name
FROM data_tab;
Another way to do it is to define the complete structure of the string
and extract the second group:
WITH data_tab AS (
SELECT '1540_INPUTTER' user_name FROM dual
UNION SELECT '1540_RAZZ25_UNKNOWN' FROM dual
UNION SELECT '1540_RAKIB17_OS_WIN10' FROM dual
)
SELECT REGEXP_SUBSTR(user_name,'(\d{4}_)([A-Z0-9]+)(_)?(\w+)?',1,1,'i',2)
AS st_user_name
FROM data_tab;
Check This.
WITH data_tab AS (
SELECT '1540_INPUTTER' user_name FROM dual
UNION SELECT '1540_RAZZ25_UNKNOWN' FROM dual
UNION SELECT '1540_RAKIB17_OS_WIN10' FROM dual
)
SELECT
case when INSTR(SUBSTR(user_name, INSTR(user_name, '_')+1, length(user_name)- INSTR(user_name, '_')+1 ),'_') =0 then
SUBSTR(user_name, INSTR(user_name, '_')+1, length(user_name)- INSTR(user_name, '_')+1 )
else
substr((SUBSTR(user_name, INSTR(user_name, '_')+1, length(user_name)- INSTR(user_name, '_')+1 )), 1, INSTR(SUBSTR(user_name, INSTR(user_name, '_')+1, length(user_name)- INSTR(user_name, '_')+1 ),'_') -1)
end as user_name
from data_tab