TO_ CHAR in oracle - sql

I am using oracle 11G and I know TO_CHAR is used to convert data into character and this query will work perfectly to get the day of the particular date:
SELECT TO_CHAR (DATE '2019-08-15', 'Day') FROM DUAL;
But this won’t work:
SELECT TO_CHAR (DATE '15-08-2019', 'Day') FROM DUAL;
ERROR: ORA-01847: day of month must be between 1 and last day of month
SELECT TO_CHAR (DATE '15-aug-19', 'Day') FROM DUAL;
ERROR: ORA-01843: not a valid month
Why the above won’t work. I have checked NLS_DATE_FORMAT it displays “DD-MON-RR”. But in first query it’s “YYYY-MM-DD”.
What am I missing here?

Because YYYY-MM-DD is the string literal format in date'YYYY-MM-DD' with respect to ANSI 92 SQL standard.
For other format types, you need to convert explicitly, such as to_date('15-08-2019','dd-mm-yyyy')

This issue is purely due to your NLS settings. if you run below query you can see the current NLS parameter values for your database
SELECT * FROM NLS_DATABASE_PARAMETERS
if you want to change your nls parameter for date please run below query
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-YYYYY';

Related

Oracle SQL Query TO_DATE

I have a question regarding the following query in Oracle SQL:
SELECT TO_DATE('10-11-2015','DD-MM-YYYY') FROM DUAL;
When I run it the result is the following 10-NOV-15. From what I read in the Oracle docs the YYYY should convert to a 4-digit year but it converts into 2-digit year. Why is this happening?
The date is being set with a 4-digit year.
However, you are being shown the date using the default format that Oracle is currently using to display dates in your user's session. The default format Oracle uses for implicit date conversions depends on the territory. For America (among others) this is DD-MON-RR and will have a 2-digit year; but for Sweden the default is RRRR-MM-DD and a 4-digit year will be displayed.
This format can be set using:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-RR';
So, if instead you set the default format to YYYY-MM-DD (the ISO-8601 date format) using:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD';
Then your query:
SELECT TO_DATE('10-11-2015','DD-MM-YYYY') FROM DUAL;
Would output 2015-11-10.
If you want a specific format for a date then you should not rely on implicit conversions as ANY user can set their own format at ANY time and you will not get consistent outputs; instead you should use an explicit conversion:
SELECT TO_CHAR( TO_DATE('10-11-2015','DD-MM-YYYY'), 'DD-MM-YYYY' ) FROM DUAL;
Would output 10-11-2015.
You can see that this isn't happening by using extract():
SELECT EXTRACT( YEAR FROM TO_DATE('10-11-2015','DD-MM-YYYY') ) FROM DUAL;
The two digit year is only how the value is being presented.
Alternative of using below can be better:
SELECT TO_DATE('10-11-2015','DD-MM-RRRR') FROM DUAL;

how to get the date format in 'DD-MM-YY' format in ORACLE without using TO_CHAR?

How do we convert a date in the 'DD-MM-YY' format WITHOUT using to_char ?
If I use the following query i get it in DD-Mon-YY format ?
select TO_DATE(SYSDATE,'DD-MM-YY') from dual ;
Output : 29-Mar-18
I want it in 29-03-18 format , without using to_char.
Is it possible ?
How do we convert a date in the 'DD-MM-YY' format WITHOUT using to_char ?
This is a common misconception that dates in the database have a format.
A date does not have a format - it is stored internally to the database as 7-bytes (representing year, month, day, hour, minute and second) and it is not until whatever user interface you are using (i.e. SQL/Plus, SQL Developer, Java, etc) tries to display it to you, the user, and converts it into something you would find meaningful (usually a string) that the date is given a format so that you, the user, find it meaningful on the client software.
So the question you should be asking is:
How do we get <insert name of SQL client software here> to change the default format it uses for a DATE data type?
If you are using SQL/Plus or SQL Developer then it will use the NLS_DATE_FORMAT session parameter to format the date. You can change this using:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-YY';
Note: This is a session parameter and will only change the format for the current session (not for any other users or any subsequent sessions).
If you want to set this as the session default then you could set a logon trigger (if users are relying on the previous default format then applying this may not be well received).
You can also change the preferences in the SQL Developer GUI as described here.
we dont have alter permission , its client db
Then use TO_CHAR( date, format_model ) - that is what it is there for.
Note: Please do not use 2-digit years as the expected format. It is a source of errors when dates are given an unexpected and wrong century.
If I use the following query i get it in DD-Mon-YY format ?
select TO_DATE(SYSDATE,'DD-MM-YY') from dual ;
TO_DATE( date_string, format_model ) takes two string arguments and Oracle will implicitly call TO_CHAR to convert your date to a string so it will match the expected data type and then when the client program formats it it will implicitly perform a similar transformation again. So your query is effectively:
SELECT TO_CHAR(
TO_DATE(
TO_CHAR(
SYSDATE,
( SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT'
)
),
'DD-MM-YY'
),
( SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT'
)
)
FROM DUAL;
If the NLS_DATE_FORMAT is MM-DD-YY then SYSDATE will be implicitly converted to the default MM-DD-YY format then explicitly converted to a date using your format DD-MM-YY, and the day/month values will be swapped, before being converted back to the default MM-DD-YY format for display. Relying on implicit conversions is prone to many errors - try to avoid it.
If you want a date for your SQL client to format using their default format then just use:
SELECT SYSDATE FROM DUAL;
If you want to get a formatted date then use TO_CHAR( date, format_model ):
SELECT TO_CHAR( SYSDATE, 'DD-MM-YY' ) FROM DUAL;
ALTER SESSION is one option:
SQL> alter session set nls_date_format = 'dd-mm-yy';
Session altered.
SQL> select sysdate from dual;
SYSDATE
--------
29-03-18
Note that what you did - applied TO_DATE function to SYSDATE - is wrong. SYSDATE already returns DATE, so you could have applied TO_CHAR to it (with appropriate format mask), but not TO_DATE.
I would suggest you to use TO_CHAR or nls_date_format , though EXTRACT is an alternative if you want to answer an interview question.
SELECT EXTRACT (DAY FROM SYSDATE)
||'-'
|| LPAD(EXTRACT (MONTH FROM SYSDATE), 2, 0)
|| '-'
|| SUBSTR( EXTRACT(YEAR FROM SYSDATE),-2) as dt
FROM DUAL;

What is Oracle's Default Date Format?

I have an Oracle DB, and I don't control the date format. I want to know what the date format is to ensure that searches like
select * from search where search_date>='03/16/2016 00:00:00'
work as expected.
Don't do that - you are relying on implicit data type conversion which is going to fail at some point.
You have two options:
1) Use a proper ANSI SQL date literal:
select *
from search
where search_date >= timestamp '2016-03-16 00:00:00';
2) use to_date() (or to_timestamp()) and use a custom format.
select *
from search
where search_date >= to_date('03/16/2016 00:00:00', 'mm/dd/yyyy hh24:mi:ss');
With to_date() you should avoid any format that is language dependent. Use numbers for the month, not abbreviations (e.g. 'Mar' or 'Apr') because they again rely on the client language.
More details can be found in the manual: https://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements003.htm#SQLRF51062
Never rely on implicit data type conversion.
You can get all the NLS session parameters with the query:
SELECT * FROM NLS_SESSION_PARAMETERS;
or, if you have the permissions GRANT SELECT ON V_$PARAMETER TO YOUR_USERNAME;, you can use the command:
SHOW PARAMETER NLS;
If you just want the date format then you can do either:
SELECT * FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT';
or
SHOW PARAMETER NLS_DATE_FORMAT;
However, you could also use ANSI date (or timestamp) literals which are format agnostic. An ANSI date literal has the format DATE 'YYYY-MM-DD' and a timestamp literal has the format TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF9'. So your query would be:
select * from search where search_date>= DATE '2016-03-16'
or
select * from search where search_date>= TIMESTAMP '2016-03-16 00:00:00'
What is Oracle's Default Date Format?
A DATE doesn't have any format. Oracle does not store dates in the format you see. It stores it internally in 7 bytes with each byte storing different components of the datetime value.
Byte Description
---- -------------------------------------------------
1 Century value but before storing it add 100 to it
2 Year and 100 is added to it before storing
3 Month
4 Day of the month
5 Hours but add 1 before storing it
6 Minutes but add 1 before storing it
7 Seconds but add 1 before storing it
To display, use TO_CHAR with proper FORMAT MODEL.
For comparing, use TO_DATE with proper FORMAT MODEL.
What you see as a format by default, is your locale specific NLS settings.
SQL> select parameter, value from v$nls_parameters where parameter='NLS_DATE_FORMAT';
PARAMETER VALUE
--------------- ----------------------------------------------------------------
NLS_DATE_FORMAT DD-MON-RR
SQL> select sysdate from dual;
SYSDATE
---------
17-MAR-16
SQL> select to_char(sysdate, 'mm/dd/yyyy hh24:mi:ss') from dual;
TO_CHAR(SYSDATE,'MM
-------------------
03/17/2016 12:48:41
SQL>
search_date>='03/16/2016 00:00:00'
You are comparing a DATE with a string literal. Always, explicitly convert the string into date using TO_DATE and proper format mask.
TO_DATE('03/16/2016', 'MM/DD/YYYY')
Or, if you dealing only with the date part and not concerned with the time portion, then use the ANSI date literal which uses a fixed format DATE 'YYYY-MM-DD'
DATE '2016-03-16'
You might just be lucky to get an output due to an implicit datatype conversion based on your locale specific NLS settings. Never ever rely on implicit datatype conversion, it might work for you, might fail for others where the nls settings are different.

ORA-01843: not a valid month error when using to_date function

SELECT sysdate as today,
to_date(CONCAT (to_char(sysdate,'MM-DD-YYYY'),
to_char(sysdate,'hh24:mi:ss'))) as time
FROM DUAL
Every time I execute this query it gives me ORA-01843: not a valid month
Could someone please help me?
As I understand, you want to see time from sysdate, if so you need this select:
SELECT sysdate as today,
to_char(sysdate,'MM-DD-YYYY hh24:mi:ss'))) as time
FROM DUAL
In your example wrong part is to_date, you should use mask for to_date:
to_date(CONCAT (to_char(sysdate,'MM-DD-YYYY'),
to_char(sysdate,'hh24:mi:ss')),'MM-DD-YYYY hh24:mi:ss')
But this will return exactly the same as sysdate because both of this columns are date and the format of dates depends on variable NLS_DATE_FORMAT, check here
to_date(CONCAT (to_char(sysdate,'MM-DD-YYYY'),
to_char(sysdate,'hh24:mi:ss'))) as time
It is simply useless what you are doing. You are extracting date and time portions separately and then converting it back to DATE. It is nothing but SYSDATE itself.
It would make sense if you are extracting and displaying the date and time elements. Or, you have date and time as string literals separately, and now you want to convert it into a DATE.
For example,
SQL> alter session set nls_date_format='mm/dd/yyyy hh24:mi:ss';
Session altered.
SQL> SELECT to_date(CONCAT ('01/11/2016', '14:45:20'), 'MM/DD/YYYYHH24:MI:SS') my_date
2 FROM DUAL;
MY_DATE
-------------------
01/11/2016 14:45:20
ORA-01843: not a valid month
Reason
This mostly happens due to NLS dependency. Since you are not using an explicit format mask, Oracle is trying to implicitly convert it based on your locale-specific NLS settings.
Example
Let's change the NLS format of the session:
SQL> alter session set nls_date_format='dd/mon/yyyyhh24:mi:ss';
Session altered.
Let's execute the same query:
SQL> SELECT to_date(CONCAT ('01/11/2016', '14:45:20')) my_date
2 FROM DUAL;
SELECT to_date(CONCAT ('01/11/2016', '14:45:20')) my_date
*
ERROR at line 1:
ORA-01843: not a valid month
As expected, it throws ORA-01843: not a valid month.

Questions on To_Date function

I have two questions,
1.
Why can't I get HH24:MI:SS when using To_date function?
select To_date(fn_adjusted_date(SUBMIT_DATE),'DD-MON-YY-HH24:MI:SS')
from HPD_Help_Desk;
16-NOV-08
select To_char(fn_adjusted_date(submit_date),'DD-MON-YY-HH24:MI:SS')
from HPD_Help_Desk;
16-NOV-08-06:01:10
2.
Why am I getting an error when using:
To_date(fn_adjusted_date(SUBMIT_DATE),'DD-MON-YY-HH24:MI:SS')
but changing it works fine when I change it to:
To_date(fn_adjusted_date(SUBMIT_DATE),'DD-MM-YY-HH24:MI:SS')
To demonstrate:
select sysdate from dual;
03-MAR-15
alter session set nls_date_format = 'dd-mm-yyyy hh24:mi:ss';
select sysdate from dual;
03-03-2015 11:29:22
select To_date(fn_adjusted_date(SUBMIT_DATE),'DD-MON-YY-HH24:MI:SS')
from HPD_Help_Desk;
ORA-01843: not a valid month 01843. 00000 - "not a valid month"
select To_char(fn_adjusted_date(submit_date),'DD-MON-YY-HH24:MI:SS')
from HPD_Help_Desk;
16-NOV-08-06:01:10
1.
Because to_date() gives you a date object, and you're leaving it up to your client to decide how to display that as a string; it's likely to be using your NLS_DATE_FORMAT settings.
Since your fn_adjusted_date() function returns a date not a string, do not then call to_date() on that; you're doing an implicit conversion to a string and then back to a date, both using NLS_DATE_FORMAT, and from how your first query is displayed - as DD-MON-YY? - that is losing the time portion anyway. So you're really doing:
select to_date(to_char(fn_adjusted_date(SUBMIT_DATE), 'DD-MON-YY'),
'DD-MON-YY-HH24:MI:SS') from HPD_Help_Desk;
2.
Because MON is the abbreviated month name in your date language. This follows on from the first point; now in the first of those you're doing an implicit to_char() of your value using the new NLS_DATE_FORMAT, which specifies the month number with MM, but then you try to convert that back to a date with MON. So this time you're really doing:
select to_date(to_char(fn_adjusted_date(SUBMIT_DATE), 'dd-mm-yyyy hh24:mi:ss'),
'DD-MON-YY-HH24:MI:SS') from HPD_Help_Desk;
And 11 is not a valid month name. Oracle is quite flexible with date formats when it can be; it can interpret 'NOV' using the MM model even though that doesn't make sense really since it isn't a number, but the meaning is pretty obvious; from your example in a comment:
select to_date('16-Nov-2008', 'DD-MM-YY') from dual;
TO_DATE('16-NOV-2008','DD-MM-YY')
---------------------------------
16-NOV-2008 00:00:00
It doesn't work the other way though; it can't interpret 11 using MON. That flexibility can appear inconsistent, and it sometimes seems to be too forgiving.
In the second query you're doing an explicit to_char() with a format model specified, which is the correct way to display a date as a string.
The underlying messages are the same for both: don't call to_date() when you already have a date object, don't ever rely on implicit conversion, and don't convert a date to a string while you're still processing it - only if you want it as a string in a specific format in your final result set.