PL/SQL extract numbers between characters - sql

I have a string in the format 12345Q999W12345. Basically, some digits followed by 'Q' followed by more digits, followed by 'W' and ends in more digits. I want to extract the number between the characters 'Q' and 'W'. The best that I have been able to come up with is:
select regexp_substr( '12345Q999W12345' , 'Q[^(\d+)$]+W' ) from dual;
The output that I get from the above is:
Q999W
Any pointers on how to further refine this regexp?

Figured it out.
select regexp_substr( '12345Q999W12345' , '\Q(\d+)\W', 1, 1, NULL, 1 ) from dual;

I'm not sure what you figured out because your regular expression (posted as an answer) doesn't return anything in my 19c Oracle database.
In the following query,
result - my suggestion (forget about regular expression; this is a simple task which is easily solved with good, old substr + instr combination)
your_1 - result of your 1st query (posted in a question)
your_2 - result of your 2nd query (posted as an answer)
SQL> select banner from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
SQL> with test (col) as
2 (select '12345Q999W12345' from dual)
3 select substr(col,
4 instr(col, 'Q') + 1,
5 instr(col, 'W') - instr(col, 'Q') - 1
6 ) result,
7 --
8 regexp_substr(col, 'Q[^(\d+)$]+W') your_1,
9 regexp_substr(col, '\Q(\d+)\W', 1, 1, NULL, 1) your_2
10 from test;
RESULT YOUR_1 YOUR_2
---------- ---------- ----------
999 Q999W
SQL>

Related

SUBSTR in sql oracle from right

I have data in table 000_ABC_AXEL. The expectation is that i have to exclude data after the last '_' and get 000_ABC in oracle sql? Any suggestions?
Need sql query to achieve below
for ex:
a_222_4 -- > expected result :a_222
123_xyz_0 -- >expected result :123_xyz
A regex replacement fits your requirement nicely:
SELECT col, REGEXP_REPLACE(col, '_[^_]+$', '') AS col_out
FROM yourTable;
You can do it with simple string functions (which are much faster than regular expressions) by finding the sub-string up to the character before the last underscore:
SELECT SUBSTR(col, 1, INSTR(col, '_', -1) - 1) AS first_parts
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (col) AS
SELECT 'a_222_4' FROM DUAL UNION ALL
SELECT '123_xyz_0' FROM DUAL;
Outputs:
FIRST_PARTS
a_222
123_xyz
fiddle

REGEXP_SUBSTR is inconsistent with extracting digits before a comma/decimal point [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 months ago.
Improve this question
my regex looks like this:
select REGEXP_SUBSTR(my_column,'[^,]+',1,1) from myTable;
this returns the results just fine:
regex
value
11
11,2
5
5,2
11
11,4
6
6,6
however when a value starts with a 0 its returns the following
regex
value
1
0,1
How can I select the 0 instead of the 1?
If you had a string with '0,1' your query would return 0, not 1.
What you're seeing suggests your column is actually a number rather than a string, your NLS_NUMERIC_CHARACTERS setting has a comma as the decimal separator, and you using implicit conversion to convert the number to a string then applying your regular expression to that.
So rather than doing any string manipulation, just truncate the values to get the integer part:
select TRUNC(my_column) from myTable;
db<>fiddle
Since you want the leading digits before the comma, you should be using ^[^,]+:
SELECT REGEXP_SUBSTR(my_column, '^[^,]+', 1, 1) FROM myTable;
Do you really need regular expressions here? It seems you want to return values that precede comma character. So, if all values follow the same pattern you posted in sample data, then substr + instr do the job:
Sample data:
SQL> with test (col) as
2 (select '11,2' from dual union all
3 select '5,2' from dual union all
4 select '11,4' from dual union all
5 select '6,6' from dual union all
6 select '0,1' from dual
7 )
Query begins here:
8 select col, substr(col, 1, instr(col, ',') - 1) result
9 from test;
COL RESU
---- ----
11,2 11
5,2 5
11,4 11
6,6 6
0,1 0
SQL>
If your values are supposed to represent numbers, you can just use TRUNC as long as your numeric separated characters are set properly for your session.
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ", ";
WITH
sample_data (my_column)
AS
(SELECT TO_NUMBER ('11,2') FROM DUAL
UNION ALL
SELECT TO_NUMBER ('5,2') FROM DUAL
UNION ALL
SELECT TO_NUMBER ('11,4') FROM DUAL
UNION ALL
SELECT TO_NUMBER ('6,6') FROM DUAL
UNION ALL
SELECT TO_NUMBER ('0,1') FROM DUAL)
SELECT trunc(my_column)
FROM sample_data;

how to handle invalid date in Oracle SQL

There is a column that displays some statements for transactions
Ex: 'date-05-03-2020-Tran-100002345-W.44321-CR-9999eu843'
And I fetched out only the date from the statement. In some cases, there is an invalid date such as date-05-032-2020 (three numbers in month instead of two numbers)
So when I executed the query I got an error as I am using TO_DATE function to convert the varchar to date after extracting it from the previous statement
So what I need is a function like IfError in excel sheet but in oracle SQL
Ex: if the DATE_x is invalid then replace it with DATE_x2
with x as(
select TO_DATE('01/20/2021','DD-MM-YYYY') DATE_x,
TO_DATE('01/02/2021','DD-MM-YYYY') DATE_x2
from dual
)
select
DATE_x2
from x;
Please advise
There is a default... on conversion error parameter in to_date: https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/TO_DATE.html#GUID-D226FA7C-F7AD-41A0-BB1D-BD8EF9440118
So you can write TO_DATE('01/20/2021' default '01-01-2020' on conversion error, 'DD-MM-YYYY') or TO_DATE('01/20/2021' default null on conversion error ,'DD-MM-YYYY').
There is also validate_conversion function: https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/VALIDATE_CONVERSION.html#GUID-DC485EEB-CB6D-42EF-97AA-4487884CB2CD
One option is to check date format with regexp_like. Example you posted suggests it is 'dd-mm-yyyy' which means [2 digits - 2 digits - 4 digits]. Certainly, [43-85-0123] "matches" such a format but doesn't represent a valid date. Anyway, see if it helps. In my example, I'm substituting an invalid date value with today's date. Read comments within code.
SQL> with test (col) as
2 -- sample data; the 2nd row is invalid
3 (select 'date-05-03-2020-Tran-100002345-W.44321-CR-9999eu843' from dual union all
4 select 'date-05-032-2020-Tran-100002345-W.44321-CR-9999eu843' from dual
5 ),
6 exdate as
7 -- extract "date" substrings (values between 1st and 4th "-" character)
8 (select col,
9 substr(col, instr(col, '-') + 1,
10 instr(col, '-', 1, 4) - instr(col, '-') - 1
11 ) datum
12 from test
13 )
14 select col,
15 case when regexp_like(datum, '[0-9]{2}-[0-9]{2}-[0-9]{4}') then
16 to_date(datum, 'dd-mm-yyyy')
17 else trunc(sysdate)
18 end as result
19 from exdate
20 /
COL RESULT
---------------------------------------------------- ----------
date-05-03-2020-Tran-100002345-W.44321-CR-9999eu843 05-03-2020
date-05-032-2020-Tran-100002345-W.44321-CR-9999eu843 01-08-2021
SQL>

using Substr and instr in SQL

I am trying to extract the middle of the string from record. I want to get the only middle of the string.
select instr('WUK00000105376:WUKE03960761:WUKR0093868603',':')
from dual;
I want to get only WUKE03960761 from this string.
I have tried by using sustring and instring to get the output but i am not getting.
SELECT SUBSTR(col,
INSTR(col, ':') + 1,
INSTR(col, ':', 1, 2) - INSTR(col, ':') - 1)
FROM dual
Another option is to use regexp_substring() to get the string between the two colons:
select regexp_substr('WUK00000105376:WUKE03960761:WUKR0093868603',':[A-Z0-9]+:')
from dual;
This however would also return the colons, but you can also tell regexp_substr() to return a specific group from the regex:
select regexp_substr('WUK00000105376:WUKE03960761:WUKR0093868603',':([A-Z0-9]+):',1,1,'i',1)
from dual;
But Tim's solution using substr and instr is most likely going to be a lot faster.

Change number format by adding decimal to it in oracle

I have a number 000005500 and I want it to be in format 0000055.00 or 55.00 using an Oracle select query.
I used this query:
select to_char(to_number(000005500),'99.99') from dual
but its displaying #####
How can I display it in the format I need?
As written your to_number() call is just doing an implicit conversion to a string and then an explicit conversion back to a number, which seems pointless, but I assume you're actually dealing with a value from a varchar2 column. In which case you see:
select to_char(to_number('000005500'),'99.99') from dual
TO_CHA
------
######
You're seeing the hashes because you can't fit your four-digit number, 5500, into a 99.99 format - you have four digits before the decimal point and the format mask only allows for two.
The bit you seem to be missing is dividing by 100 to get the decimal:
select to_char(to_number('000005500') / 100,'99.99') from dual;
TO_CHA
------
55.00
Another approach, if you want to keep it as a string with the same number of leading zeros as the oroginal value, is to leave it as a string, chop it up with substr(), and concatenate the parts back together. Using a CTE as a demo:
with t as (select '000005500' as val from dual)
select val, substr(val, 1, length(val) - 2)
|| '.' || substr(val, length(val) - 1, 2) as adj_val
from t;
VAL ADJ_VAL
--------- ---------------------------------------------
000005500 0000055.00
Firstly, 000005500 is not a number. A number doesn't start with zero. You are dealing with a string.
I want it to be in format 0000055.00
You can only expect it to be a string as an output, and not a number.
Anyway, to get the output as 55.00 as NUMBER, you could do the following -
SQL> WITH DATA AS(
2 SELECT '000005500' num FROM DUAL
3 )
4 SELECT to_char(to_number(replace(num,'0','')),'99D99') FROM DATA
5 /
TO_CHA
------
55.00
SQL>
Or,
SQL> WITH DATA AS(
2 SELECT '000005500' num FROM DUAL
3 )
4 SELECT to_char(to_number(rtrim(ltrim(num,'0'),'0')),'99D99') FROM DATA
5 /
TO_CHA
------
55.00
SQL>
Edit
Alex's method is also nice, it uses simple mathematics to convert it to DECIMAL. I would prefer his way for the first part.
Another couple of alternatives around the final formatting:
select to_char(to_number('000005500')/100,'0999999D90') leading_zeros,
to_char(to_number('000005500')/100,'fm9999999D90') no_leading_zeros
from dual;