null values not processed by to_timestamp in oracle sql - sql

I'm trying to load data from a csv with sql-loader. There's one column with date in this format:
2011-12-31 00:00:00.000
I tried it using to_date() but it couldn't handle fractions of second. Therefore I used this:
cast(TO_TIMESTAMP(:DATUM_ONTVANGST, 'YYYY-MM-DD HH24:MI:SS.FF3')as date)
Now I get the error:
ORA-01841: (full) year must be between -4713 and +9999, and not be 0
for null values in the column
Can to_timestamp not handle null values or am I doing something wrong?

This works:
select
cast(TO_TIMESTAMP('2011-12-31 00:00:00.000', 'YYYY-MM-DD HH24:MI:SS.FF3')as date)
from dual
and this also works
select
cast(TO_TIMESTAMP(null, 'YYYY-MM-DD HH24:MI:SS.FF3')as date)
from dual
So there must be some value that is not right format

I think there is space(<field>, ,<field>) in your data which is causing the issue.
Better to use TRIM before using them.
Following are some examples to demonstrate with different values:
-- working fine with correct timestamp values
SQL> SELECT
2 CAST(TO_TIMESTAMP('2019-11-05 00:00:00.000', 'YYYY-MM-DD HH24:MI:SS.FF3') AS DATE) as dt
3 FROM
4 DUAL;
DT
---------
05-NOV-19
-- working fine with a null value
SQL>
SQL> SELECT
2 CAST(TO_TIMESTAMP(NULL, 'YYYY-MM-DD HH24:MI:SS.FF3') AS DATE) as dt
3 FROM
4 DUAL;
DT
---------
-- working fine with an empty value
SQL>
SQL> SELECT
2 CAST(TO_TIMESTAMP('', 'YYYY-MM-DD HH24:MI:SS.FF3') AS DATE) as dt
3 FROM
4 DUAL;
DT
---------
Now showing the error and solution
-- giving an error with space in the value -- Your case
-- might be you are considering it as the null
-- but it is actually a value that is a space character
SQL>
SQL> SELECT
2 CAST(TO_TIMESTAMP(' ', 'YYYY-MM-DD HH24:MI:SS.FF3') AS DATE) as dt
3 FROM
4 DUAL;
CAST(TO_TIMESTAMP(' ', 'YYYY-MM-DD HH24:MI:SS.FF3') AS DATE) as dt
*
ERROR at line 2:
ORA-01841: (full) year must be between -4713 and +9999, and not be 0
-- Solution to your issue -- using TRIM
SQL>
SQL> SELECT
2 CAST(TO_TIMESTAMP(TRIM(' '), 'YYYY-MM-DD HH24:MI:SS.FF3') AS DATE) as dt
3 FROM
4 DUAL;
DT
---------
SQL>
Cheers!!

Building off Ersin's answer, this is the format that worked for me
select
cast(NULL AS TIMESTAMP WITH LOCAL TIME ZONE) as date
from dual

Related

Use NVL with date conversion function returns ORA-01843: not a valid month error

I have the below SQL (Oracle) that is erroring with ORA-01843: not a valid month error when I do not pass dates into the :P_FROM_DATE and :P_TO_DATE parameters. For some reason it is not accepting the alternate date values (sysdate-30, sysdate-1).
It does work properly if I pass dates into the two parameters. How can I modify this to work?
AND pha.CREATION_DATE BETWEEN
NVL(to_date(to_char((:P_FROM_DATE),'YYYY-MM-DD')||' 00:00:00','YYYY-MM-DD HH24:Mi:SS') , sysdate-30)
AND
NVL( to_date(to_char((:P_TO_DATE),'YYYY-MM-DD')||' 23:59:59','YYYY-MM-DD HH24:Mi:SS') , sysdate-1)
That's because you applied NVL function on the wrong place. You should apply it to parameters themselves, not the whole expression:
AND pha.CREATION_DATE BETWEEN
to_date(nvl(:P_FROM_DATE, to_char(sysdate - 30, 'yyyy-mm-dd')) ||' 00:00:00', 'YYYY-MM-DD HH24:Mi:SS')
AND
to_date(nvl(:P_TO_DATE , to_char(sysdate - 1 , 'yyyy-mm-dd')) ||' 23:59:59', 'YYYY-MM-DD HH24:Mi:SS');
For example (in which I use SQL*Plus and substitution variables; you'd still use bind ones in your code):
SQL> with pha (name, creation_date) as
2 (select 'Little', date '2022-11-01' from dual union all
3 select 'Foot' , date '2019-08-25' from dual
4 )
5 select *
6 from pha
7 where 1 = 1
8 AND pha.CREATION_DATE BETWEEN
9 to_date(nvl('&P_FROM_DATE', to_char(sysdate - 30, 'yyyy-mm-dd')) ||' 00:00:00', 'YYYY-MM-DD HH24:Mi:SS')
10 AND
11 to_date(nvl('&P_TO_DATE' , to_char(sysdate - 1 , 'yyyy-mm-dd')) ||' 23:59:59', 'YYYY-MM-DD HH24:Mi:SS');
Enter value for p_from_date: --> NULL values passed to
Enter value for p_to_date: --> both parameters
NAME CREATION_D
------ ----------
Little 2022-11-01
SQL>
Or, with values passed to the query:
SQL> /
Enter value for p_from_date: 2019-07-13
Enter value for p_to_date: 2019-09-30
NAME CREATION_D
------ ----------
Foot 2019-08-25
SQL>

how to fetch records between two timestamps in oracle?

I have a column of TIMESTAMP(6) datatype in oracle and have values like 2022-04-01 18:02:42 and i wanna fetch all the records that falls between two datetime. I tried like below but no luck,
select * from table
where column BETWEEN '2022-04-01 18:02:42' and '2022-11-03 19:28:57' -- no records
tried this too,
select *
from table
where column BETWEEN to_date('2022-04-01','yyyy-mm-dd')
and to_date('2022-11-03','yyyy-mm-dd') -- a non-numeric character was found where a numeric was expected
and,
select *
from table
where column BETWEEN to_timestamp('2022-04-01','yyyy-mm-dd')
and to_timestamp('2022-11-03','yyyy-mm-dd') -- a non-numeric character was found where a numeric was expected.
I want to fetch records falls in this dates!
Thanks.
You need to convert the literal into DATE using TO_DATE and required format mask to compare the timestamp column with the input timestamp values.
SQL> CREATE TABLE t(A TIMESTAMP);
Table created.
SQL>
SQL> INSERT INTO t(A) VALUES(to_date('2022-04-10T15:39:00', 'YYYY-MM-DD"T"HH24:MI:SS'));
1 row created.
SQL> INSERT INTO t(A) VALUES(to_date('2022-05-01T15:39:00', 'YYYY-MM-DD"T"HH24:MI:SS'));
1 row created.
SQL> INSERT INTO t(A) VALUES(to_date('2022-03-01T15:39:00', 'YYYY-MM-DD"T"HH24:MI:SS'));
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM t;
A
----------------------------
10-APR-22 03.39.00.000000 PM
01-MAY-22 03.39.00.000000 PM
01-MAR-22 03.39.00.000000 PM
SELECT *
FROM t
WHERE A BETWEEN
to_date('2015-04-06T15:39:00', 'YYYY-MM-DD"T"HH24:MI:SS')
AND
to_date('2022-05-06T15:39:00', 'YYYY-MM-DD"T"HH24:MI:SS');
A
--------------------------------------------------------------------------
10-APR-22 03.39.00.000000 PM
01-MAY-22 03.39.00.000000 PM
Use TIMESTAMP literals:
SELECT *
FROM table_name
WHERE column_name BETWEEN TIMESTAMP '2022-04-01 18:02:42'
AND TIMESTAMP '2022-11-03 19:28:57';
or DATE and INTERVAL DAY TO SECOND literals:
SELECT *
FROM table_name
WHERE column_name BETWEEN DATE '2022-04-01' + INTERVAL '18:02:42' HOUR TO SECOND
AND DATE '2022-11-03' + INTERVAL '19:28:57' HOUR TO SECOND;
or TO_TIMESTAMP:
SELECT *
FROM table_name
WHERE column_name BETWEEN TO_TIMESTAMP('2022-04-01 18:02:42', 'YYYY-MM-DD HH24:MI:SS')
AND TO_TIMESTAMP('2022-11-03 19:28:57', 'YYYY-MM-DD HH24:MI:SS');
or TO_DATE:
SELECT *
FROM table_name
WHERE column_name BETWEEN TO_DATE('2022-04-01 18:02:42', 'YYYY-MM-DD HH24:MI:SS')
AND TO_DATE('2022-11-03 19:28:57', 'YYYY-MM-DD HH24:MI:SS');
Firstly have to change NLS_DATE_FORMAT as follows:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD';
ALTER SESSION SET NLS_DATE_FORMAT = 'HH24:MI:SS';
Then running the query using TIMESTAMP literal as follows:
SELECT * FROM table_name WHERE column_name BETWEEN TIMESTAMP '2022-04-01 18:02:42' AND TIMESTAMP '2022-11-03 19:28:57';

Inject character to date column

I have a date column.
select RETAIL_ACQUISITION_DTTM from XXXXX.TABLE_2348
13/07/2018
I want to select the value as such it returns as below,
2018-07-13T00:00:00
so it has to display the date as YYYY-MM-DD with T and then HH24:MI:SS
You need to convert your string (why is it stored as a string?) to a date using a format model that matches the actual string value:
to_date(RETAIL_ACQUISITION_DTTM, 'DD/MM/YYYY')
Having that format model wrong is why you get the 0013 year in your result.
Then you can convert that date back to a string with to_char(), and you can embed the fixed T as a character literal with double quotes, using a format model like 'YYYY-MM-DD"T"HH24:MI:SS':
with TABLE_2348 (RETAIL_ACQUISITION_DTTM) as (
select '13/07/2018' from dual
)
select to_char(to_date(RETAIL_ACQUISITION_DTTM, 'DD/MM/YYYY'),
'YYYY-MM-DD"T"HH24:MI:SS') as RETAIL_ACQUISITION_DTTM
from XXXXX.TABLE_2348;
RETAIL_ACQUISITION_
-------------------
2018-07-13T00:00:00
You could also just ignore that it is a date and use string manipulation:
with TABLE_2348 (RETAIL_ACQUISITION_DTTM) as (
select '13/07/2018' from dual
)
select substr(RETAIL_ACQUISITION_DTTM, 7, 4)
|| '-' || substr(RETAIL_ACQUISITION_DTTM, 4, 2)
|| '-' ||substr(RETAIL_ACQUISITION_DTTM, 1, 2)
|| 'T00:00:00' as RETAIL_ACQUISITION_DTTM
from XXXXX.TABLE_2348;
RETAIL_ACQUISITION_
-------------------
2018-07-13T00:00:00
If the column is actually a date rather than a string then you are doing unnecessary conversions, including implicit ones which rely on your NLS settings, and you are losing the original time from the value if it was not midnight anyway:
alter session set nls_date_format = 'DD-MON-RR';
with TABLE_2348 (RETAIL_ACQUISITION_DTTM) as (
select to_date('2018-07-13 12:34:56', 'YYYY-MM-DD HH24:MI:SS') from dual
)
select to_char(to_date(RETAIL_ACQUISITION_DTTM, 'YYYY-MM-DD-HH24:MI:SS'),
'YYYY-MM-DD HH24:MI:SS') as RETAIL_ACQUISITION_DTTM from XXXXX.TABLE_2348;
RETAIL_ACQUISITION_
-------------------
0013-07-20 18:00:00
That is really doing:
to_char(
to_date(
to_char(
RETAIL_ACQUISITION_DTTM,
'DD-MON-RR'), ---- from your session NLS_DATE_FORMAT setting
'YYYY-MM-DD-HH24:MI:SS'),
'YYYY-MM-DD HH24:MI:SS')
If you skip to extra steps you can just format the date directly:
with TABLE_2348 (RETAIL_ACQUISITION_DTTM) as (
select to_date('2018-07-13 12:34:56', 'YYYY-MM-DD HH24:MI:SS') from dual
)
select to_char(RETAIL_ACQUISITION_DTTM,
'YYYY-MM-DD"T"HH24:MI:SS') as RETAIL_ACQUISITION_DTTM
from XXXXX.TABLE_2348;
RETAIL_ACQUISITION_
-------------------
2018-07-13T12:34:56
which also doesn't rely on your NLS settings, so won't break in interesting ways if it's run in another session with different settings.
replace sysdate with your column name if its date type.
Using Single to_char:
select to_char(sysdate,'--YYYY-MM-DD"T"hh24:mi:ss') from dual;
Using two to_char and concatenation for simplification.
select to_char(sysdate,'--'||'YYYY-MM-DD')||'T'||to_char(sysdate,'hh24:mi:ss') from dual; -- 13/07/2018
select '--'||to_char(sysdate,'YYYY-MM-DD')||'T'||to_char(sysdate,'hh24:mi:ss') from dual; -- 13/07/2018

Not able to convert into date in oracle

I am trying to convert the date field, but the year is not correct.
My code is:
SELECT to_date((ADD_MONTHS(TRUNC(sysdate, 'MM'), -1) + 25), 'dd/mm/yyyy')
FROM dual
Expected output is like 26/02/2018, but I get 26/02/0018.
Do I miss anything?
You really shouldn't apply TO_DATE function on a DATE datatype value, which is what SYSDATE is.
Saying that you have to compare the result of your query with another DATE datatype value will be just fine, as you'll compare two dates.
As of incorrect format you get: in presentation layer, you'd use TO_CHAR function and format the result anyway you want; for example:
SQL> SELECT
2 to_char((ADD_MONTHS(TRUNC(sysdate, 'MM'), -1) + 25), 'dd/mm/yyyy') val_1,
3 to_char((ADD_MONTHS(TRUNC(sysdate, 'MM'), -1) + 25), 'mon dd yyyy') val_2
4 FROM dual;
VAL_1 VAL_2
---------- --------------------
26/02/2018 vel 26 2018
SQL>
Note that both values are strings, not dates. Moreover, see a strange VAL_2 month? It is in Croatian, but there's a way out of it:
SQL> SELECT
2 to_char((ADD_MONTHS(TRUNC(sysdate, 'MM'), -1) + 25),
3 'mon dd yyyy',
4 'nls_date_language = english') val_2
5 FROM dual;
VAL_2
--------------------
feb 26 2018
SQL>
or even
SQL> alter session set nls_date_language = english;
Session altered.
SQL> SELECT
2 to_char((ADD_MONTHS(TRUNC(sysdate, 'MM'), -1) + 25), 'mon dd yyyy') val_2
3 FROM dual;
VAL_2
--------------------
feb 26 2018
SQL>
but - once again - this is just to present data. There's nothing you should do with your code, except from removing TO_DATE function (optionally, you can alter session in order to see date the way you want it, but it won't change anything but visual appearance):
SQL> alter session set nls_date_format = 'dd/mm/yyyy';
Session altered.
SQL> select ADD_MONTHS(TRUNC(sysdate, 'MM'), -1) + 25 val_3
2 from dual;
VAL_3
----------
26/02/2018
SQL>
If you want the date in a particular format, use to_char():
SELECT to_char((ADD_MONTHS(TRUNC(sysdate, 'MM'), -1) + 25), 'dd/mm/yyyy') from dual
The result of:
SELECT ADD_MONTHS(TRUNC(sysdate, 'MM'), -1) + 25 from dual
is a date and it is correct. The problem is converting the date to a date.

Difference between two dates

I have a table that has the following data
fromDate | toDate
20JAN11 | 29DEC30
Both dates are for the 21st Century (i.e. 2011 and 2030) but only the last two characters are stored.
Why is the following statement (when run from within a PL/SQL module) against the above data always returns a positive value
dateDifference := (fromDate - toDate)
If i run the following statement from sqlplus i get the correct negative value which is correct.
select to_date('20JAN11','DDMONYY')-to_Date('29DEC30','DDMONYY') from dual;
I remember reading somewhere that Oracle would sometimes use the wrong century but i dont quite remember the exact scenario where that would happen.
Assuming those columns are of DATE datatype, which seems to be the case: Oracle always stores DATE values in an internal format which includes the full year. The fact that you are seeing only a 2-digit year has to do with the date format used to convert the date to a string for display. So most likely the stored century values are not what you think they are.
Try selecting the dates with an explicit format to see what you really have stored:
SELECT TO_CHAR( fromDate, 'DD-MON-YYYY' ), TO_CHAR( toDate, 'DD-MON-YYYY' )
Seems to work for me either way on my 10g database:
SQL> set serveroutput on
SQL>
SQL> DECLARE
2 d1 DATE := to_date('20JAN11','DDMONRR');
3 d2 DATE := to_date('29DEC30','DDMONRR');
4 diff INTEGER;
5 BEGIN
6 diff := d1 - d2;
7 dbms_output.put_line(diff);
8 END;
9 /
-7283
PL/SQL procedure successfully completed
SQL>
EDIT: works for YY instead of RR year format as well.
EDIT2: Something like this, you mean?
SQL> create table t (d1 date, d2 date);
Table created
SQL> insert into t values (to_date('20JAN11','DDMONYY'), to_date('29DEC30','DDMONYY'));
1 row inserted
SQL> commit;
Commit complete
SQL>
SQL> DECLARE
2 R t%ROWTYPE;
3 diff INTEGER;
4 BEGIN
5 SELECT d1, d2
6 INTO R
7 FROM t;
8 diff := R.d1 - R.d2;
9 dbms_output.put_line(diff);
10 END;
11 /
-7283
PL/SQL procedure successfully completed
SQL>
As #Alex states, you may want to verify your data.
works without formatting as well
CREATE TABLE DATETEST(FROMDATE DATE, TODATE DATE);
insert into DATETEST (fromdate,todate) values (to_date('20Jan11','ddMonrr'),to_date('29DEC30','ddMonrr'));
SELECT TO_CHAR(FROMDATE,'ddMonrrrr hh24:mi:ss') FROMDATE,
TO_CHAR(TODATE,'ddMonrrrr hh24:mi:ss') TODATE
from datetest ;
/*
FROMDATE TODATE
------------------ ------------------
20Jan2011 00:00:00 29Dec2030 00:00:00
*/
set serveroutput on
DECLARE
l_FROMDATE DATETEST.FROMDATE%type ;
L_TODATE DATETEST.TODATE%TYPE;
dateDifference number;
BEGIN
--notice -- no formatting just putting them into a variable for test
SELECT FROMDATE, TODATE
INTO L_FROMDATE, L_TODATE
from datetest;
DATEDIFFERENCE := L_FROMDATE - L_TODATE ;
DBMS_OUTPUT.PUT_LINE('DATEDIFFERENCE = ' || DATEDIFFERENCE );
end ;
--DATEDIFFERENCE = -7283
SELECT FROMDATE-TODATE
from datetest ;
/* --still not formatting
FROMDATE-TODATE
----------------------
-7283
*/
SELECT (FROMDATE - TODATE) DATEDIFF,
TO_CHAR(FROMDATE,'ddMonrrrr') FROMDATE,
to_char(todate,'ddMonrrrr') todate
from (
SELECT TO_DATE('20JAN11','DDMONYY') FROMDATE,
TO_DATE('29DEC30','DDMONYY') TODATE
FROM DUAL)
;
/*
DATEDIFF FROMDATE TODATE
---------------------- --------- ---------
-7283 20Jan2011 29Dec2030
*/
try running the first query on your table:
SELECT TO_CHAR(FROMDATE,'ddMonrrrr hh24:mi:ss') FROMDATE,
TO_CHAR(TODATE,'ddMonrrrr hh24:mi:ss') TODATE
from datetest ;
see if the years are what you actually expect.
(Edit: changed to use two digit years)