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>
Related
I write a code about to determine incorret data but I get error messages. Info about data, date columns have type of number, so I have to change them to date. Ex: 10322->01.03.2022
Here is my code;
select *
from (select *****_date,
*****_hold_no,
case
when length(*****_end_date) = 5 then
to_date(lpad(*****_end_date, 6, '0'), 'ddmmyy')
else
to_date(*****_end_date, 'ddmmyy')
end as *****_end_date,
case
when length(*****_start_date) = 5 then
to_date(lpad(*****_start_date, 6, '0'), 'ddmmyy')
else
to_date(*****_start_date, 'ddmmyy')
end as *****_start_date
from ods_****.*****
where *****_KW_GLAC = ' '
and *****_KW_CAAC is not null) A
where *****_end_date<*****_start_date
and it turns the error of "day of month must be between 1 and last day of month".
But if I change the condition of _end_date<_start_date to _end_date>_start_date, I do not get any error message and query works. What can be the problem? Thanks
Reason behind the error message
Do not store dates as strings; change the table to store them as DATEs and then you can only store valid values and do not have to perform any type conversions.
However, since you have them as strings, don't use a CASE expression and instead, just use LPAD on all the values to ensure everything has the correct length (and, from Oracle 12.2, can use VALIDATE_CONVERSION to check that the can be converted to a date):
SELECT *
FROM (
SELECT obfuscated_date,
obfuscated_hold_no,
TO_DATE(LPAD(obfuscated_end_date, 6, '0'), 'ddmmyy') AS obfuscated_end_date,
TO_DATE(LPAD(obfuscated_start_date, 6, '0'), 'ddmmyy') AS obfuscated_start_date
FROM ods_obfuscated.obfuscated
WHERE obfuscated_KW_GLAC = ' '
AND obfuscated_KW_CAAC is not null
AND VALIDATE_CONVERSION(LPAD(obfuscated_start_date, 6, '0') AS DATE, 'ddmmyy') = 1
AND VALIDATE_CONVERSION(LPAD(obfuscated_end_date, 6, '0') AS DATE, 'ddmmyy') = 1
) A
WHERE obfuscated_end_date < obfuscated_start_date
You can also see the invalid values using:
SELECT obfuscated_date,
obfuscated_hold_no,
obfuscated_end_date,
obfuscated_start_date
FROM ods_obfuscated.obfuscated
WHERE obfuscated_KW_GLAC = ' '
AND obfuscated_KW_CAAC is not null
AND ( VALIDATE_CONVERSION(LPAD(obfuscated_start_date, 6, '0') AS DATE, 'ddmmyy') = 0
OR VALIDATE_CONVERSION(LPAD(obfuscated_end_date, 6, '0') AS DATE, 'ddmmyy') = 0
)
(at least) one of TO_DATE function calls fails because value you're working with is evaluated to invalid date.
For example, for January (which has 31 days), you got 32 or 56 or 93 or ...
That kind of problems happens when people store date values as strings - now you have to deal with that.
Error message looks like Oracle; you didn't specify which version you use. If possible, see on conversion error, such as
SQL> with test (datum) as
2 (select '13.10.2022' from dual union all
3 select '32.01.2023' from dual --> invalid
4 )
5 select datum,
6 to_date(datum default null on conversion error, 'dd.mm.yyyy') date_value
7 from test;
DATUM DATE_VALUE
---------- ----------
13.10.2022 13/10/2022
32.01.2023 --> NULL, as DATUM is invalid
SQL>
Now it is easy to filter out values that can't be converted to dates.
I have a situation where I need to convert a numeric column value into time. It's a 6 digit field, but unfortunately, different processes over the years inserted data in different format, some HHMM, and others HHMMSS. Let's call this column colTime. I use colTime in combination with another 8 digits numeric field which contains a date in YYYYMMDD format, let's call this colDate.
It is being used as below to construct a TIMESTAMP in UTC zone:
select TO_CHAR(SYS_EXTRACT_UTC(TO_TIMESTAMP(CONCAT(NULLIF(colDate,0), LPAD(NULLIF(colTime,0),4,0)), 'YYYY-MM-DD HH24:MI')), 'YYYY-MM-DD"T"HH24:MI:SS.FFTZR') from tab1;
The problem here obviously is that the colTime may contain 4 OR 6 digit data so I cannot know the correct LPAD number in advance. When the above statement encounters a 6 digit field it throws an error.
I was thinking if I have a function similar to COALESCE that can execute the second argument if the first one returns an error then I'd be able to accommodate LPAD 4 and 6 cases.
I can use a CASE statement, but was hoping for something more graceful.
You can still use COALESCE if you also use the DEFAULT NULL ON CONVERSION ERROR syntax in TO_TIMESTAMP:
select
coalesce
(
to_timestamp('2021-01-01' || ' ' ||the_time default null on conversion error, 'YYYY-MM-DD HHMI'),
to_timestamp('2021-01-01' || ' ' ||the_time default null on conversion error, 'YYYY-MM-DD HHMISS')
)
from
(
select '0102' the_time from dual union all
select '010203' the_time from dual
);
You can use regular expressions to find out which format the data comes from and then you can convert it appropriately.
For example:
with
d (col_time) as (
select '0215' from dual
union all select '183207' from dual
union all select '12:34:56' from dual
union all select 'really-bad-data' from dual
)
select d.*,
case when regexp_like(col_time, '^[0-9]{4}$') then 'Short Time Format'
when regexp_like(col_time, '^[0-9]{6}$') then 'Long Time Format'
else 'Other Time Format'
end as guessed_format
from d;
Result:
COL_TIME GUESSED_FORMAT
---------------- -----------------
0215 Short Time Format
183207 Long Time Format
12:34:56 Other Time Format
really-bad-data Other Time Format
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>
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=94771b6589b01526ad0cf6e5c4d01945
I need help in extracting the number substring from a file name
currently for file format - 'monkey_eats_mango_everyday_202002.txt'
we are doing like this
select regexp_substr('monkey_eats_mango_everyday_202002.txt', '\d+') as parameter12a
from dual;
result-
202002
which in turn used in larger query to get the last date of this date like this
select to_char(last_day(to_date(regexp_substr('monkey_eats_mango_everyday_202002.txt', '\d+'), 'yyyymm')), 'yyyymmdd') as parameter
from dual ;
result-
20200229
Now the file format has changed, so we have - 'donkey_eats_pines_cones_20192301_7771234_everyday_202002.txt'
In this file format there are numbers at other places like 201943_7771234 which can be dates or any random number, so I need regex expression which can extract 202002 from file format
select regexp_substr('donkey_eats_pines_cones_201943_7771234_everyday_202002.txt', '\d+') as parameter12a
from dual;
You can use a \. to anchor your digits match to next to the period in the file name, and then use a capture group around the digits to get just the digits in the output, using the 6th parameter to REGEXP_SUBSTR to indicate that you only want group 1 in the output:
SELECT REGEXP_SUBSTR('donkey_eats_pines_cones_201943_7771234_everyday_202002.txt', '(\d+)\.', 1, 1, NULL, 1) AS parameter12a
FROM dual;
Output:
202002
Demo on dbfiddle
One option is to use nested expressions: inner returns file extension and the date (that precedes that extension), and outer fetches date itself.
SQL> with test (col) as
2 (select 'donkey_eats_pines_cones_201943_7771234_everyday_202002.txt' from dual)
3 select regexp_substr(regexp_substr(col, '\d+.\w+$'), '\d+') result From test
4 /
RESULT
------
202002
SQL>
check this
select reverse(split_part(reverse(r.r ), '.', 2)) from
(
SELECT reverse(split_part(reverse('donkey_eats_pines_cones_20192301_7771234_everyday_202002.txt'), '_', 1)) as r
)as r
ANS :
202002
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;