Oracle Implicit Conversion of String to date? - sql

I work in T-SQL but have been given some Oracle PL-SQL for review on a Project.
Within the code there are Multiple WHERE clauses that do comparison of a Field of DataType = DATE against Strings which hold a "date".
ex:
WHERE to_date(mytable.mydatefield) > '23-OCT-2015'
OR
WHERE mytable.mydatefield > '23-OCT-2015'
Q1: Since "mydatefield" is already defined as a DATA type, isn't doing a "to_date" unnecessary?
Q2: Will Oracle do an implicit conversion on the '23-OCT-2015' and convert it to a date for comparison? I seem to remember encountering this before and comparing DATES to STRINGS caused issues?
Am I incorrect about that? If not can someone give me an example that I can use as evidence that it would not work?

A1: In general yes, but take the way Oracle handles implicit type conversions into account. The To_Date function around the mydatefield column expects a string input, so Oracle implicitly converts mydatefield to a string with a format matching the NLS_DATE_FORMAT session setting (which defaults to DD-MON-RR). Once converted to a string the To_Date function then converts it back to a date again using the current NLS_DATE_FORMAT setting. The newly reconstituted date is then compared to the string '23-OCT-2015', but since dates and strings aren't directly comparable the string value gets implicitly converted to a date using the current NLS_DATE_FORMAT setting. Depending on the value of the NLS_DATE_FORMAT setting, the first implicit conversion is likely to lose information specifically any time portion AND the original century, since the default NLS_DATE_FORMAT uses only a two digit year RR and no time component.
A2: Possibly, but it's best not to rely on it.
Both relations are poor programming for a couple of reasons. First they both are affected by implicit type conversions from dates to strings (or vice versa). Second they are both attempting to compare dates with strings in a non canonical form. As such 10-DEC-15 is less than 23-OCT-2015 because 1 is less than 2. Also note the difference in the number of digits representing the year since the default NLS_DATE_FORMAT uses a two digit year.
The correct method would be to compare the date column (possibly truncated) to a date string explicitly converted to a date
WHERE mytable.mydatefield > TO_DATE('23-OCT-2015', 'DD-MON-YYYY')
OR with truncation:
WHERE trunc(mytable.mydatefield) > TO_DATE('23-OCT-2015', 'DD-MON-YYYY')
which removes the time component of the date field.

Q1: According to Oracle, the first parameter of to_date() is a char value. Using it like to_date(date_value) you will force an implicit cast of date_value to char and then wrapping it again in a date value.
Q2: The server will do an implicit conversion from the string '23-OCT-2015' to a date value but based on database parameters which can differ from various servers(DEV vs PROD for example) so you should not rely on them. An example of correct usage would be WHERE mytable.mydatefield > to_date('23-OCT-2015','dd-MON-yyyy')
You should always use to_date/to_char to make sure you are using the correct format. Please see this answer for a more detailed explanation: Comparing Dates in Oracle SQL

Related

Insert date and time in formats mm/dd/yyyy and time in the format hh24:mm:ss in plsql

Lets say I have a table Student with columns Name,DOJ,TOJ.
Inorder to enter date in mm/dd/yyyy format and timestamp in the format hh24:mm:ss I used ALTER SESSION SET NLS_DATE_FORMAT='MM/DD/YYYY' and ALTER NLS_TIMESTAMP_FORMAT='HH24:MI:SS' but i want to know an alternative solution to enter in this format without involving session. Please guide me through this.
CLICK HERE TO VIEW COLUMNS AND THEIR DATA TYPES
We store dates/times either in a DATE column (which is Oracle's inappropriate name for a datetime) or a TIMESTAMP column (which has more precision and can handle timezones, too). These types have no format. This is important, because thus comparing and sorting them in the database works fine, because the DBMS knows how to handle datetimes, while the users see the date in their format. I, for instance, have set my Windows to German, so I will see the datetimes in some German format, while you will see the same date in a format you are used to.
There are few situations where you want to store date and time separately. The reason is typically that you can set them null. A date without a time means "the whole day". A time without a date means "every day this time". But then you often want this even more advanced anyway ("every Tuesday and Wednesday", "every December 24", etc.) for which you need soemthing more sophisticated then just date and time split into two.
So, usually we just store date and time combined. For a precision down to seconds we use DATE. If we wanted them separately we'd have to think of an appropriate data type, because Oracle has no real date type and no time type either. So we'd either use DATE and ignore the date part or the time part or we use strings. The former solution is usually preferred, because you cannot mistakenly enter invalid data like February 30 or 23:66:00.
If you want to store formatted dates and times, you are talking about strings. I don't recommend this. Strings are not the appropriate data types for dates and times. And a format '01/02/2000' is ambiguous. Some people will read this as February 1, others as January 2.
If you wanted to do this, you would have to change the column types to VARCHAR2 and simply store values like '02/25/2021' and '13:28:56'.
But if you wanted to sort your data or compare dates/times then or just show them to a foreign user in a format they are used to, you would always have to convert them. E.g.:
select *
from mytable
order by to_date(doj || ' ' || toj, 'mm/dd/yyyy hh24:mi:ss');
I am afraid that to change default format you have no other option but to change NLS_DATE_FORMAT either in database level or session level.
But If your purpose is to show the date in a specific format in the front end then you can use to_char() function as below:
SELECT NAME, to_CHAR(DOJ,'dd/mm/yyyy'),to_CHAR(TOJ,'HH24:MI:SS') FROM table
To change the default date format in system level:
ALTER SYSTEM SET NLS_DATE_FORMAT='DD/MM/YYYY' scope=both;
You can also change the default date format at startup time with a login trigger to change the nls_date_format:
CREATE OR REPLACE TRIGGER CHANGE_DATE_FORMAT
AFTER LOGON ON DATABASE
call DBMS_SESSION.SET_NLS('NLS_DATE_FORMAT','YYYYMMDD');

Hive dates: date 'yyyy-MM-dd' vs. 'yyyy-MM-dd' What is the difference?

What is the difference between referring to a hive date as date '2020-08-25' vs. just '2020-08-25' without the word date? Is it two different data types? or are these the exactly the same. This something I might put into a where statement like below:
where somedate<=date'2020-08-25'
vs.
where somedate<='2020-08-25'
date'2020-08-25' is a standard date literal: it generates a legitimate value of the date datatype.
On the other hand, '2020-08-25' is a literal string (that represents a date, but it could be anything else).
Now what is the best pick for the predicate in your where clause? It depends on the datatype of the column you want to compare.
If you have a date column, then I would recommend using a literal date. Otherwise, you rely on the ability of the database to understand what you mean, and implicitly convert the string to a date. Each database has its own set of rules to handle such case: in the worst case scenario, it might take the wrong decision and decide do string comparison, which would require converting all stored values to string (this depends on the database specification) - which would kill the performance of the query.
If you happen to be storing dates as strings (which usually indicates a bad design), then string comparison is fine.
date '2020-08-25' is a literal of type DATE.
'2020-08-25' - is a string literal.
String and dates can be implicitly converted, so both your where clauses are functionally identical. Depending on column datatype, implicit conversion may happen. Better use the same type to avoid implicit conversion.
Also DATE can be packed in 4 bytes integer in binary formats. See HIVE-3910

TO_CHAR and TO_DATE use and the right context to use them

If I have a query like:
select *
from CAT_ACCT_AUDIT_TRAIL cataccount0_
where cataccount0_.CAAT_EXECUTED_DATE >=TO_DATE(’26-AUG-2016′, ‘DD-MM-YYYY’) AND
to_Date(TO_CHAR(cataccount0_.CAAT_EXECUTED_DATE , ‘dd -mon-yyyy’), ‘DD-MM-YYYY’)<=TO_DATE('31-AUG-2016', 'DD-MM-YYYY')
Here why do we require the to_char or to_date functions? What is the right context to use them?
If I do either of these:
select TO_DATE('26-AUG-2016', 'DD-MM-YYYY') from dual;
select TO_DATE('01-12-2016', 'DD-MM-YYYY') from dual;
I get the output in NLS variable format as I set in the session, irrespective of date input in format conversion; I get the same result for both. Wy is this so?
What is the correct way to solve this query? I mean when i need to fetch the values in date range.
You use to_date() to convert a string like '01-12-2016' to the date datatype. You use to_char() to convert a date to a string. In both cases you specify the format of the string - if you don't then your session NLS settings are used, which is not good practice for anything except ad hoc queries as someone else running your code later may get a different output or an error.
A general rule - which your code is following - is to compare data of one type with values/constants of the same type. As your column is a date, you're supplying the filter values as dates - by converting strings to the date datatype. If you didn't do that then implicit conversion would happen, but you should not rely on that either as it can also lead to NLS issues, and depending on the type it can prevent indexes being used. Read more about data conversion in the documentation.
Oracle tries to be flexible when interpreting the string when you do to_date(). When you do TO_DATE('26-AUG-2016', 'DD-MM-YYYY') you are supplying the month as a string (in a specific language, which is another topic), but telling the function to expect a number. Oracle 'helpfully' interprets that anyway, so it usually works. But whatever format you use for to_date(), you aren't specifying the display format, so your client is deciding how to display the converted date as a string for you - usually using your NLS settings, again.
Doing this:
to_Date(TO_CHAR(cataccount0_.CAAT_EXECUTED_DATE , ‘dd -mon-yyyy’), ‘DD-MM-YYYY’)
is usually pointless, but even so should be using consistent format models. One reason this is sometimes done is if the source date (caat_executed_date here) has its time set to something other than midnight, and you want to discard the time. But there are better ways to do that - specifically the trunc() function, which by default sets the time to midnight.
When you have constant values, like TO_DATE('31-AUG-2016', 'DD-MM-YYYY'), you can also use ANSI date literals, in the form of DATE '2016-08-31'.
It is unclear what you want to do, but you don't actually need those functions on constants. Just use the date keyword for date literals. For instance:
where cataccount0_.CAAT_EXECUTED_DATE >= date '2016-08-26'
If you want to remove the time component from a date, then use trunc():
where trunc(cataccount0_.CAAT_EXECUTED_DATE, 'dd') -- the `'dd'` is optional for this purpose
This can be used in any context where a date constant is accepted.

Oracle - Should I convert dates with to_date before comparison?

In the queries I stumble upon each date is converted with to_date function before any comparison. Sometimes it caused "literal does not match format string" error, which had rather nothing to do with format and the cause was explained here:
ORA-01861: literal does not match format string
My question is: is it really necessary to use date conversion? Why is it converted in the first place before applying any logical comparison?
Oracle does not store dates as, well, dates. The problem is that there might be a time on the dates that would cause them to be unequal. (You can see the documentation here for information about the date data type.)
In general, we think that "2013-01-01" is equal to "2013-01-01". However, the first date might be "2013-01-01 01:00:00" and the second "2013-01-01 02:02:02". And they would not be equal. To make matters worse, they may look the same when they are printed out.
You don't actually have to convert the dates to strings in order to do such comparisons. You can also use the trunc() function. Such a transformation of the data is insurance against "invisible" time components of the data interfering with comparisons.
You should really be storing dates as actual dates (or timestamps). If you have strings representing dates, you will often need to convert them using to_date (with a specified format, not relying on default formats). It really depends on what comparisons/date functionality you want. You're getting errors because you hit a value that does not conform to your specified format. This is also a good reason to specify a column as DATE to store dates. For example,
select to_date('123', 'MM-DD-YYYY') from dual;
will throw an ORA-01861. So you may have 99.9% of the rows as MM-DD-YYYY, but the 0.1% will cause you headaches.
Anyway, if you cleanup those strings, you can do much more using to_date and date functions. For example:
select
(last_day(to_date('02-05-2009', 'MM-DD-YYYY')) - to_date('01-15-1998', 'MM-DD-YYYY')) as days_between_dates
from dual;
Not fun to do that with strings. Or maybe just find the most recent date:
select greatest( to_date('02-05-2009', 'MM-DD-YYYY'), to_date('12-01-1988', 'MM-DD-YYYY')) from dual;
using string comparison would give wrong answer:
select greatest('02-05-2009', '12-01-1988') from dual;
Just a few examples, but much better to treat dates as dates, not strings.
If you have a string that represents a date, use TO_DATE.
If you already have a date, use it directly.

Access SQL Date Format

How can I retrieve a record based on a Date property? I'm trying:
WHERE Meetings.[MDate] = '16/12/2011'
which is the format I use but I get :
"Data type mismatch in criteria expression"
Problem solved: It should have been:
WHERE Meetings.[MDate] = 16/12/2011
No quotation marks.
For where clauses use
columnName = #mm/dd/yyyy#
You'll want to use the SQL date format: '#2011-12-16#'
Use the cast to DATETIME function, CDATE(), which will honour the machine's regional settings. That said, it still a good idea to use an unambiguous date format and the ISO 8601 format is a good one.
Also note that Access doesn't have a date data type: its sole temporal data type is DATETIME and, as its name suggests, always has a time element accurate to one second time granule, even if that time happens to be midnight. Therefore, it is a good idea to always include a time value to one second time granule in all DATETIME literals e.g.
WHERE Meetings.MDate = CDATE('2011-12-16 00:00:00');
Another advantage to the above is that the Access UI will not attempt to reformat the DATETIME literal because it is held as a string.