Invalid number format model - sql

SELECT decode(to_number(to_char(01-04-2016, 'mm')),
1,
to_number(to_char(01-04-2016, 'yyyy')) - 1,
2,
to_number(to_char(01-04-2016, 'yyyy')) - 1,
3,
to_number(to_char(01-04-2016, 'yyyy')) - 1,
to_number(to_char(01-04-2016, 'yyyy'))) FROM DUAL;
whenever i execute this statement it shows invalid number format model.

Your query is invalid currently, as to_char expects a number or date, but the plain number 01-04-2016 doesn´t represent either of these.
The follow up mentioned in your comment to_char(01042016,'mm') wont work either, as the given number can´t be parsed with mm as it does expect a valid numberFormat and not a dateFormat, which mm isn´t.
In order for your query to work, wrap the pure date into a to_date and mention the current dateFormat given by the char:
SELECT decode(to_number(to_char(to_date('01-04-2016', 'dd-mm-yyyy'), 'mm')),
1,
to_number(to_char(to_date('01-04-2016', 'dd-mm-yyyy'), 'yyyy')) - 1,
2,
to_number(to_char(to_date('01-04-2016', 'dd-mm-yyyy'), 'yyyy')) - 1,
3,
to_number(to_char(to_date('01-04-2016', 'dd-mm-yyyy'), 'yyyy')) - 1,
to_number(to_char(to_date('01-04-2016', 'dd-mm-yyyy'), 'yyyy')))
FROM DUAL;

Related

How to convert VARCHAR2 to INTERVAL

I want to convert a VARCHAR2-value like '-28:15:00' to INTERVAL.
With a literal value, this works:
select interval '-09:11:36' hour to second from dual;
However, this does not (ORA-00923: FROM keyword not found where expected):
select interval MY_VARCHAR hour to second from MY_TABLE;
--comparable to select interval to_char(sysdate, 'hh:mm:ss') hour to second from dual;
My assumption is that the literal value is implicitly cast while the explicit varchar-value from MY_VARCHAR (or char from to_char respectively) is not valid between "interval" and "hour".
CAST like this does not work (ORA-00963: unsupported interval type):
select cast(MY_VARCHAR as interval hour to second) from MY_TABLE;
--comparable to select cast('09:11:36' as interval hour to second) from dual;
What does work is concatenating '0 ' as the day-value and cast it to INTERVAL DAY TO SECOND:
select cast('0 ' || '09:11:36' as interval day to second) from dual;
However this only works for positive values, and as long as the value for hour is below 24.
Is there a better solution than dissecting the VARCHAR-value with CASE, SUBSTR and so on?
You need the minus sign before the days to cast it to an interval:
SELECT value,
CAST( REGEXP_REPLACE(value, '^(-)?', '\10 ') AS INTERVAL DAY TO SECOND )
AS interval_value
FROM table_name
or, using simple string functions, which slightly more to type but probably more efficient (as regular expressions are slow):
SELECT value,
CAST(
CASE
WHEN value LIKE '-%'
THEN '-0 ' || SUBSTR(value, 2)
ELSE '0 ' || value
END
AS INTERVAL DAY TO SECOND
) AS interval_value
FROM table_name
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT '-09:11:36' FROM DUAL UNION ALL
SELECT '09:11:36' FROM DUAL;
Both output:
VALUE
INTERVAL_VALUE
-09:11:36
-00 09:11:36.000000
09:11:36
+00 09:11:36.000000
fiddle
Another approach, which would accept hour values greater than 23, is to coerce the string into ISO format and use the to_dsinterval() function:
select my_varchar,
to_dsinterval(
regexp_replace(my_varchar, '(-?)(\d+):(\d+):(\d+)', '\1PT\2H\3M\4S')
) as interval_value
from my_table
MY_VARCHAR
INTERVAL_VALUE
-09:11:36
-000000000 09:11:36.000000000
09:11:36
+000000000 09:11:36.000000000
28:15:02
+000000001 04:15:02.000000000
-28:15:02
-000000001 04:15:02.000000000
fiddle
You could do reformatting of the string with string functions, which as #MTO mentioned will be faster for large amounts of data, but it would be a bit messy:
select my_varchar,
to_dsinterval(
case
when substr(my_varchar, 1, 1) = '-'
then '-PT' || substr(my_varchar, 2, instr(my_varchar, ':', 1, 1) - 2)
else 'PT' || substr(my_varchar, 1, instr(my_varchar, ':', 1, 1) - 1)
end || 'H'
|| substr(my_varchar, instr(my_varchar, ':', 1, 1) + 1,
instr(my_varchar, ':', 1, 2) - instr(my_varchar, ':', 1, 1) - 1) || 'M'
|| substr(my_varchar, instr(my_varchar, ':', 1, 2) + 1) || 'S'
) as interval_value
from my_table
MY_VARCHAR
INTERVAL_VALUE
-09:11:36
-000000000 09:11:36.000000000
09:11:36
+000000000 09:11:36.000000000
28:15:02
+000000001 04:15:02.000000000
-28:15:02
-000000001 04:15:02.000000000
fiddle
As shown in the documentation, interval literals (like all literals, as the name implies) require a fixed value, not an expression or column; the quotes are not optional in the railroad diagram:
... so your first query gets ORA-00923.
And the cast() function only supports certain conversions:
Note 1: Datetime/interval includes DATE, TIMESTAMP, TIMESTAMP WITH TIMEZONE, TIMESTAMP WITH LOCAL TIME ZONE, INTERVAL DAY TO SECOND, and INTERVAL YEAR TO MONTH.
so you can't cast(... as interval hour to second), and that will throw ORA-00963.

Changing Odd Text String to Date SQL

Have a text string that I need to convert to date, however, it's not the generic yyyymmdd string. e.g. March 2018 is stored as '2018m3' as are all other dates. Is there a way to convert the string to a normal date in Oracle?
This may be helpful :
SQL> SELECT to_date('2018m3', 'YYYY"m"MM') FROM dual
2 /
Output
TO_DATE('2018M3','YYYY"M"MM')
-----------------------------
3/1/2018
If that is the format, you can do:
select add_months(to_date(substr(col, 1, 4) || '0101'),
to_number(replace(substr(col, -2), 'm', '')) - 1
)
An alternative method:
select to_date(substr(col, 1, 4) || regexp_substr(col, '[0-9]+', 1, 2), 'YYYYMM')

How to change date format to 'YYYY/DD/MM'?

i got financial year 1st month April and 1st week date using (:weekno) from this query
select TO_CHAR(ADD_MONTHS((Select ((TRUNC (Trunc(sysdate,'yyyy')+( :weekno)*7,'IW'))+1) fdt from duaL),+3))
from dual
Output got:03-APR-17
but i want output this format 'YYYY/DD/MM' 04/03/2017
You are using dual twice. There is no need for that.
Use to_char with yyyy/mm/dd format to get 2017/03/04:
select
to_char(
add_months(
trunc(
trunc(sysdate,'YY') + :week_no * 7, 'IW'
) + 1, 3
), 'yyyy/mm/dd'
) dt
from dual;
Use to_char with dd/mm/yyyy format to get 04/03/2017:
select
to_char(
add_months(
trunc(
trunc(sysdate,'YY') + :week_no * 7, 'IW'
) + 1, 3
), 'mm/dd/yyyy'
) dt
from dual;
If you need date value:
select to_date(TO_CHAR(ADD_MONTHS((Select ((TRUNC (Trunc(sysdate,'yyyy')+( :weekno)*7,'IW'))+1) fdt from duaL),+3)),'yyyy/mm/dd') from dual
If need string value:
select TO_CHAR(ADD_MONTHS((Select ((TRUNC (Trunc(sysdate,'yyyy')+( :weekno)*7,'IW'))+1) fdt from duaL),+3),'yyyy/mm/dd')from dual

Date Error Invalid Number

I would like to add 10 minutes to sysdate,
select to_char(SYSDATE,'dd-Mon-yyyy hh:mi:ss') + 10/1440 from dual
when I tried the above I got the error
ORA-01722: invalid number
The error appears because you add 10/1440 to char not to date.
Try this:
select SYSDATE + 10/1440 from dual;
or
select to_char(SYSDATE+ 10/1440,'dd-Mon-yyyy hh:mi:ss') from dual;
or
select to_char( sysdate + interval '10' minute,'dd-Mon-yyyy hh:mi:ss')
from dual;
Here you can find more information.
Here you can find similar problem on SO, there are more solutions.
You don't need to "to_char" the date first.
Either
select SYSDATE + 10/1440 from dual;
or
select to_char(SYSDATE + 10/1440,'dd-Mon-yyyy hh:mi:ss') from dual;
depending on whether you just want a date or a string representation of the date formatted in a certain way.

Regarding sql instr function

Oracle 10g is the db.
The below query fails when extracting the date.
SELECT TO_CHAR ( TO_DATE ( SUBSTR (file_name , INSTR (file_name , '_', -1, 2)+ 2, 8), 'YYYYMMDD'), 'DD-MM-YYYY') from dual;
I noticed we receive the below two file name of different naming formats.
660.ASSD.0063.20100923.0409.PTH2.IAC1.gcr_H201009231416151671_bbor.ddr
660.ASSD.M2K_20110309121547_489.ddr
For one file the above query works . The other file 660.ASSD.M2K_20110309121547_489.ddr
it extracts "01103091" and does a to_date fails. How can i modify this query so it works for both the file formats.
Use REGEXP_SUBSTR
select TO_CHAR(TO_DATE(SUBSTR(REGEXP_SUBSTR('660.ASSD.0063.20100923.0409.PTH2.IAC1.gcr_H201009231416151671_bbor.ddr', '\d{14,17}'), 0, 8), 'yyyymmdd'), 'dd-mm-yyyy')
from dual;
select TO_CHAR(TO_DATE(SUBSTR(REGEXP_SUBSTR('660.ASSD.M2K_20110309121547_489.ddr', '\d{14,17}'), 0, 8), 'yyyymmdd'), 'dd-mm-yyyy')
from dual;
You can also use REGEXP_REPLACE to strip-out letters from the file name.
SELECT TO_CHAR ( TO_DATE ( SUBSTR (regexp_replace(file_name, '[A-Z][a-z]', '')
, INSTR (regexp_replace(file_name, '[A-Z][a-z]', '') , '_', -1, 2)+ 1, 8), 'YYYYMMDD'), 'DD-MM-YYYY')
FROM dual;