Not able to convert into date in oracle - sql

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.

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>

null values not processed by to_timestamp in oracle 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

SQL check day of week

How to check if it's a weekend in SQL?
I know I can convert sysdate to a number using this
SQL> select to_char(sysdate, 'd') from dual;
TO_CHAR(SYSDATE,'D')
But I'm not really sure how to check if today is a 6 or 7.
Do not use TO_CHAR with the D format model for this as it is dependant on the NLS_TERRITORY session parameter.
For example, when SYSDATE = 2018-09-10 (a Monday):
ALTER SESSION SET NLS_TERRITORY = 'France';
SELECT TO_CHAR( SYSDATE, 'D' ) FROM DUAL;
Outputs 1 but the same query in a different territory:
ALTER SESSION SET NLS_TERRITORY = 'America';
SELECT TO_CHAR( SYSDATE, 'D' ) FROM DUAL;
Outputs 2.
Instead, you can use TRUNC and the IW format model:
SELECT TRUNC( SYSDATE ) - TRUNC( SYSDATE, 'IW' ) FROM DUAL
Outputs 0 for Monday (and 1 for Tuesday ... 6 for Sunday) and is independent of the NLS_TERRITORY setting.
So you could filter this to give weekends as:
SELECT *
FROM DUAL
WHERE TRUNC( SYSDATE ) - TRUNC( SYSDATE, 'IW' ) IN ( 5, 6 )
or
SELECT *
FROM DUAL
WHERE SYSDATE - TRUNC( SYSDATE, 'IW' ) >= 5
If you want the days 1-indexed for consistency with your expected output from TO_CHAR (rather then 0-indexed) then just add 1 to the value.
sysdate is a pseudo column. You don't need to query it, you can evaluate it directly:
IF TO_CHAR(SYSDATE, 'D') IN ('6', '7') THEN
-- Do something
END IF;
I would avoid the ambiguous 'D' format as this varies between territories (the week starts after the weekend where I live), and use
if to_char(sysdate,'fmDY','nls_date_language=English') like 'S%'
then
Regarding the 'D' format, unfortunately to_char doesn't let you specify nls_territory inline, so without an explicit alter session command, it will rely on the session settings at runtime. I've seen production bugs due to this, where the same code worked in London but failed in New York.
And here's the logic in a reusable function, but flipped to ask "Is it a weekday?" (not a weekend). You could add a parameter for the start day of the weekdays (2 is the default in Oracle; Sunday is day #1).
CREATE OR REPLACE FUNCTION is_weekday (date_in IN DATE)
RETURN BOOLEAN
IS
BEGIN
RETURN TO_CHAR (date_in, 'D') BETWEEN 2 AND 6;
END;
/
DECLARE
l_date DATE := DATE '2018-09-10';
BEGIN
DBMS_OUTPUT.put_line ('If your weekend is Saturday and Sunday....');
FOR indx IN 1 .. 7
LOOP
DBMS_OUTPUT.put_line (
TO_CHAR (l_date, 'FMDay, Month DD YYYY')
|| ' is '
|| CASE WHEN NOT is_weekday (l_date) THEN 'not ' END
|| 'a weekday');
l_date := l_date + 1;
END LOOP;
END;
/
Try it in LiveSQL:
https://livesql.oracle.com/apex/livesql/file/content_G8NQSY6NP48NPJX96RLQ51SUE.html

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

reperesent AM or PM values with time in oracle

i have a table with two date columns: time_from,time_to.Both have datatype of date.Now,I want to concatenate these column values such that I can get only times like this:
9:00 PM-9:50 PM.
Now,some of my times are in AM while others are PM and I want to write a select statement to concatenate them.I wrote this but it is not returning "AM" or "PM" correct everytime.
My query is:
select
to_char(time_from,'HH:MI:SS PM') || ' - ' || to_char(time_to,'HH:MI:SS PM')
from
table-name
I had initially inserted my time in 12 hr format.
Need some guidance here please? I don't want to hard code AM or PM
Now,some of my times are in AM while others are PM
DATE is not stored in the database the way you see it. What you see is only for display purpose for us to interpret it. Oracle stores date in an internal format of 7 bytes which is it's proprietary format.
but it is not returning "AM" or "PM" correct everytime
You need to validate your data as TO_CHAR doesn't manipulate the data. It will display the datetime in your desired format.
It only depends on the DATE value in your table, and is independent of the format model AM/PM in TO_CHAR.
-- setting nls_date_format only for the DATE value display
SQL> alter session set nls_date_format='MM/DD/YYYY HH:MI:SS PM';
Session altered.
PM format:
SQL> WITH DATA AS
2 ( SELECT SYSDATE - 4/24 from_date, SYSDATE + 4/24 to_date FROM dual
3 )
4 SELECT from_date,
5 to_date,
6 TO_CHAR(from_date, 'HH:MI PM')
7 ||' - '
8 ||TO_CHAR(to_date, 'HH:MI PM') my_tm_format
9 FROM DATA;
FROM_DATE TO_DATE MY_TM_FORMAT
---------------------- ---------------------- -------------------
11/12/2015 07:39:05 AM 11/12/2015 03:39:05 PM 07:39 AM - 03:39 PM
AM format:
SQL> WITH DATA AS
2 ( SELECT SYSDATE - 4/24 from_date, SYSDATE + 4/24 to_date FROM dual
3 )
4 SELECT from_date,
5 to_date,
6 TO_CHAR(from_date, 'HH:MI AM')
7 ||' - '
8 ||TO_CHAR(to_date, 'HH:MI AM') my_tm_format
9 FROM DATA;
FROM_DATE TO_DATE MY_TM_FORMAT
---------------------- ---------------------- -------------------
11/12/2015 07:39:14 AM 11/12/2015 03:39:14 PM 07:39 AM - 03:39 PM
NOTE:
The WITH clause is only to create the sample data for demonstration. Use the following query with your table, which is the same as you are using currently. The point is that you need to validate your data as TO_CHAR doesn't manipulate the data.
SELECT from_date,
to_date,
TO_CHAR(from_date, 'HH:MI PM')
||' - '
||TO_CHAR(to_date, 'HH:MI PM') my_tm_format
FROM your_table;
TO_CHAR is capable to convert the time element in 12-hour format irrespective of the format model you specify for the time element.
According to desired output (no leading zero on hour) it should be this one:
SELECT
TO_CHAR(time_from, 'fmHH:')||TO_CHAR(time_from, 'MI PM') || ' - ' ||
TO_CHAR(to_from, 'fmHH:')||TO_CHAR(to_from, 'MI PM')
FROM table_name;
or
SELECT
REGEXP_REPLACE(TO_CHAR(time_from, 'HH:MI PM'), '^0') || ' - ' ||
REGEXP_REPLACE(TO_CHAR(to_from,'HH:MI PM'), '^0')
FROM table_name;