How do I only have to input data once? - sql

I am currently using the regexp_subtr function to take a string of data (user input) and convert it into a list. Is there a way I can make it so I don't have to input the data twice. This is what I currently have:
select regexp_substr((WITH X AS (SELECT ('&EmployeeID') a FROM DUAL) SELECT REPLACE(X.a,' ',',') FROM X),'[^,]+', 1, level) "Employee"
from dual
connect by regexp_substr ((WITH X AS (SELECT ('&EmployeeID') a FROM DUAL) SELECT REPLACE(X.a,' ',',') FROM X),'[^,]+', 1, level)
is not null;

Yes, you can clean it up a bit like this:
WITH X AS
(SELECT REPLACE('&EmployeeID',' ',',') a FROM DUAL)
select regexp_substr(X.a,'[^,]+', 1, level) "Employee"
from X
connect by regexp_substr(X.a,'[^,]+', 1, level) is not null;

Using xmltable with tokenize:
select *
from xmltable('tokenize(replace(.," ",","),",")'
passing '&EmployeeID'
columns s varchar2(100) path '.');
DBFiddle: https://dbfiddle.uk/?rdbms=oracle_21&fiddle=19ab9c444502f6e1fd3bdaa44e04ab27

It appears your client is sqlplus, where the ampersand ('&') is an indication to the client to prompt for user input. You can avoid being re-prompted for the same variable by adding a second ampersand in front of the variable name.
select regexp_substr((WITH X AS (SELECT ('&&EmployeeID') a FROM DUAL) SELECT REPLACE(X.a,' ',',') FROM X),'[^,]+', 1, level) "Employee"
from dual
connect by regexp_substr ((WITH X AS (SELECT ('&&EmployeeID') a FROM DUAL) SELECT REPLACE(X.a,' ',',') FROM X),'[^,]+', 1, level)
is not null;

Related

Split a String which do not have a delimiter in Oracle

I have been beating my head around a problem. Following is the input string
1034536455702130340053769240340002208520191202134036
What I need to do is split this string into the following
03453645570
03400537692
03400022085
Here, every string that needs to get picked starts with a '03'.
I can do it with a PL/SQL code, by picking each substring starting from a '03' in a loop, then concatenating each value after removing extra characters from left and right and getting only 11 characters in each iteration. And then use REGEXP_SUBSTR to get desired result. However, this approach involves too much code. Is there a way by which this can be achieved using an SQL query?
SELECT UPPER (
REGEXP_SUBSTR ('03453645570,03400537692,03400022085',
'[^,]+',
1,
LEVEL))
AS VAL
FROM DUAL
CONNECT BY REGEXP_SUBSTR ('03453645570,03400537692,03400022085',
'[^,]+',
1,
LEVEL)
IS NOT NULL
You can use your existing code with the original input string, and just change the regex to match 03 followed by 9 digits:
SELECT REGEXP_SUBSTR ('1034536455702130340053769240340002208520191202134036',
'03[0-9]{9}',
1,
LEVEL)
AS VAL
FROM DUAL
CONNECT BY REGEXP_SUBSTR ('1034536455702130340053769240340002208520191202134036',
'03[0-9]{9}',
1,
LEVEL)
IS NOT NULL
Output
VAL
03453645570
03400537692
03400022085
Demo on dbfiddle
Using #Nicks solution. Following is the code that I have used to optimize it. I have evaluated only 1k records and it takes less than 1 seconds. I hope it helps.
--Table to store all sorts of strings
INSERT INTO TBL_SCRM_MSISDN
SELECT '0342244357903452274515236320191201091147' NUMBERS FROM DUAL
UNION
SELECT '03457064700420191201124242' FROM DUAL
UNION
SELECT '03414221723620191201130431' FROM DUAL
UNION
SELECT '1034536455702130340053769240340002208520191202134036' FROM DUAL;
-- Table used to store unique values
create table TBL_MSISDN
(
msisdn VARCHAR2(500)
);
--Using a loop to evaluate a single value one at a time
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE TBL_MSISDN';
FOR C IN
(
SELECT * FROM TBL_SCRM_MSISDN
)
LOOP
INSERT INTO TBL_MSISDN
SELECT REGEXP_SUBSTR (C.NUMBERS,
'03[0-9]{9}',
1,
LEVEL)
AS VAL
FROM DUAL
CONNECT BY REGEXP_SUBSTR (C.NUMBERS,
'03[0-9]{9}',
1,
LEVEL) is not null;
END LOOP;
commit;
END;
/
SELECT * FROM TBL_MSISDN WHERE MSISDN IS NOT NULL;
Try Below query
select trim(regexp_substr('1$03453645570213$034005376924$0340002208520191202134$036','[^$]+', 1, level) ) value, level
from dual
connect by regexp_substr('103453645570213$034005376924$0340002208520191202134$036', '[^$]+', 1, level) is not null
order by level;

REGEXP_REPLACE to replace emails in a list except a specific domain

I am novice to regular expressions. I am trying to remove emails from a list which do not belong to a specific domain.
for e.g. I have a below list of emails:
John#yahoo.co.in , Jacob#gmail.com, Bob#rediff.com,
Lisa#abc.com, sam#gmail.com , rita#yahoo.com
I need to get only the gmail ids:
Jacob#gmail.com, sam#gmail.com
Please note we may have spaces before the comma delimiters.
Appreciate any help!
This could be a start for you.
SELECT *
FROM ( SELECT REGEXP_SUBSTR (str,
'[[:alnum:]\.\+]+#gmail.com',
1,
LEVEL)
AS SUBSTR
FROM (SELECT ' John#yahoo.co.in , Jacob.foo#gmail.com, Bob#rediff.com,Lisa#abc.com, sam#gmail.com , sam.bar+stackoverflow#gmail.com, rita#yahoo.com, foobar '
AS str
FROM DUAL)
CONNECT BY LEVEL <= LENGTH (REGEXP_REPLACE (str, '[^,]+')) + 1)
WHERE SUBSTR IS NOT NULL ;
Put in a few more examples, but an email checker should comply to the respective RFCs, look at wikipedia for further knowledge about them https://en.wikipedia.org/wiki/Email_address
Inspiration from https://stackoverflow.com/a/17597049/869069
Rather than suppress the emails not matching a particular domain (in your example, gmail.com), you might try getting only those emails that match the domain:
WITH a1 AS (
SELECT 'John#yahoo.co.in , Jacob#gmail.com, Bob#rediff.com,Lisa#abc.com, sam#gmail.com , rita#yahoo.com' AS email_list FROM dual
)
SELECT LISTAGG(TRIM(email), ',') WITHIN GROUP ( ORDER BY priority )
FROM (
SELECT REGEXP_SUBSTR(email_list, '[^,]+#gmail.com', 1, LEVEL, 'i') AS email
, LEVEL AS priority
FROM a1
CONNECT BY LEVEL <= REGEXP_COUNT(email_list, '[^,]+#gmail.com', 1, 'i')
);
That said, Oracle is probably not the best tool for this (do you have these email addresses stored as a list in a table somewhere? If so then #GordonLinoff's comment is apt - fix your data model if you can).
Here's a method using a CTE just for a different take on the problem. First step is to make a CTE "table" that contains the parsed list elements. Then select from that. The CTE regex handles NULL list elements.
with main_tbl(email) as (
select ' John#yahoo.co.in , Jacob.foo#gmail.com, Bob#rediff.com,Lisa#abc.com, sam#gmail.com , sam.bar+stackoverflow#gmail.com, rita#yahoo.com, foobar '
from dual
),
email_list(email_addr) as (
select trim(regexp_substr(email, '(.*?)(,|$)', 1, level, NULL, 1))
from main_tbl
connect by level <= regexp_count(email, ',')+1
)
-- select * from email_list;
select LISTAGG(TRIM(email_addr), ', ') WITHIN GROUP ( ORDER BY email_addr )
from email_list
where lower(email_addr) like '%gmail.com';

Regexp_replace processing result

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
;

oracle SQL: get every 2nd value of a string

I am searching for a way to get every 2nd value of a string via SQL.
My string looks like this:
12:115:22:98
and I would like to get every 2nd value out of it, in this case 115 and 98.
Tried around with regexp_substr, but best I could do was to get every value with this:
select regexp_substr('3:113:1:14','[^ :]+', 2, level)
from dual
connect by regexp_substr('3:113:1:14','[^ :]+', 2, level)
is not null;
or just the 2nd value with this:
select regexp_substr('3: 113:1:14','[^ :]+', 2, 1)
from dual;
Is there a way or maybe another function to get this to work?
Thanks in advance,
Wod
edit:
it would also be possible to make the string look like this:
4-116:3-113:22-12
You were already so very very close...
select * from(
select regexp_substr('3:113:1:14:5:6:7:8','[^ :]+', 2, level), level lvl
from dual
connect by regexp_substr('3:113:1:14:5:6:7:8','[^ :]+', 2, level)
is not null
) where mod(lvl,2)=1;
Using the normal SUBSTR and INSTR :
SQL> WITH DATA AS
2 ( SELECT '12:115:22:98' STR FROM DUAL
3 UNION ALL
4 SELECT '3:113:1:14' FROM DUAL
5 )
6 SELECT SUBSTR(STR, INSTR(STR,':',1,1)+1, INSTR(STR,':',1,2)-INSTR(STR,':',1,1)-1)
7 ||'-'
8 || SUBSTR(STR, INSTR(STR,':',1,3)+1)
9 ||':'
10 ||str str
11 FROM data
12 /
STR
--------------------------------------
115-98:12:115:22:98
113-14:3:113:1:14

Split string and use in Like Command

I have a string like 'from+google+world'
I need to search the text for the entire three keywords at any place
for single query we are using,
select * from tabl name
where desc like '%from%'
and desc like '%google%'
and desc like '%world%'
How do I split the string and use it with the above query.
#p1+#p2+#p3
select * from tabl name
where desc like '%#p1%'
and desc like '%#p2%'
and desc like '%#p3%'
Radu Gheorghiu : This command is 50 % helpful it was just splitting the String with De-Limiter i have to get the column which is having 3 splitted words for eg we have to get Desc like 'all #p3 things are #p1 going #p2 fine' and desc not like 'all #p3 things are #p1 going fine'
This is the code you need and here is a SQLFiddle
with test as
(select 'from+google+world' str from dual
)
select regexp_substr (str, '[^+]+', 1, rownum) split
from test
connect by level <= length (regexp_replace (str, '[^+]+')) + 1
Do you mean this output??
from
google
world
WITH t AS (SELECT 'from+google+world' word FROM dual)
SELECT regexp_substr(word, '[[:alpha:]]+', 1, LEVEL) FROM T
CONNECT BY regexp_substr(word, '[[:alpha:]]+', 1, level) is not null;
Do you mean this output??
from
google
world
WITH t AS (SELECT 'from+google+world' word FROM dual)
SELECT regexp_substr(word, '[[:alpha:]]+', 1, LEVEL) FROM T
CONNECT BY regexp_substr(word, '[[:alpha:]]+', 1, level) is not null;
or if you're searching inside the table that contains any of these word,
you can you CONTAINS function from oracle that is indexed:
Link here :http://docs.oracle.com/cd/B28359_01/text.111/b28303/query.htm
And CTXCAT search here: http://www.oracle.com/technetwork/database/enterprise-edition/ctxcat-primer-090555.html
SELECT * FROM tablename WHERE desc IN (
SELECT regexp_substr('from+google+world','[^+]+', 1, level) FROM dual
CONNECT BY regexp_substr('from+google+world', '[^+]+', 1, level)
is not null
);