How to compare date in Oracle? - sql

I'm having issues with what I assumed would be a simple problem, but googling isn't helping a great load. Possibly I'm bad at what I am searching for nether the less.
SELECT ORDER_NUMB, CUSTOMER_NUMB, ORDER_DATE
FROM ORDERS
WHERE FORMAT(ORDER_DATE, 'DD-MMM-YYYY') = '07-JUN-2000';
It tells me I am using an invalid identifier. I have tried using MON instead of MMM, but that doesn't help either.
Unsure if it makes any difference but I am using Oracle SQL Developer.

There are multiple issues related to your DATE usage:
WHERE FORMAT(ORDER_DATE, 'DD-MMM-YYYY') = '07-JUN-2000';
FORMAT is not an Oracle supported built-in function.
Never ever compare a STRING with DATE. You might just be lucky, however, you force Oracle to do an implicit data type conversion based on your locale-specific NLS settings. You must avoid it. Always use TO_DATE to explicitly convert string to date.
WHERE ORDER_DATE = TO_DATE('07-JUN-2000','DD-MON-YYYY','NLS_DATE_LANGUAGE=ENGLISH');
When you are dealing only with date without the time portion, then better use the ANSI DATE Literal.
WHERE ORDER_DATE = DATE '2000-06-07';
Read more about DateTime literals in documentation.
Update
It think it would be helpful to add some more information about DATE.
Oracle does not store dates in the format you see. It stores it
internally in a proprietary format in 7 bytes with each byte storing
different components of the datetime value.
BYTE Meaning
---- -------
1 Century -- stored in excess-100 notation
2 Year -- " "
3 Month -- stored in 0 base notation
4 Day -- " "
5 Hour -- stored in excess-1 notation
6 Minute -- " "
7 Second -- " "
Remember,
To display : Use TO_CHAR
Any date arithmetic/comparison : Use TO_DATE
Performance Bottleneck:
Let's say you have a regular B-Tree index on a date column. now, the following filter predicate will never use the index due to TO_CHAR function:
WHERE TO_CHAR(ORDER_DATE, 'DD-MM-YYYY') = '07-06-2000';
So, the use of TO_CHAR in above query is completely meaningless as it does not compare dates, nor does it delivers good performance.
Correct method:
The correct way to do the date comparison is:
WHERE ORDER_DATE = TO_DATE('07-JUN-2000','DD-MON-YYYY','NLS_DATE_LANGUAGE=ENGLISH');
It will use the index on the ORDER_DATE column, so it will much better in terms of performance. Also, it is comparing dates and not strings.
As I already said, when you do not have the time element in your date, then you could use ANSI date literal which is NLS independent and also less to code.
WHERE ORDER_DATE = DATE '2000-06-07';
It uses a fixed format 'YYYY-MM-DD'.

try this:
SELECT ORDER_NUMB, CUSTOMER_NUMB, ORDER_DATE
FROM ORDERS
WHERE trunc(to_date(ORDER_DATE, 'DD-MMM-YYYY')) = trunc(to_date('07-JUN-2000'));

I do not recognize FORMAT as an oracle function.
I think you meant TO_CHAR.
SELECT ORDER_NUMB, CUSTOMER_NUMB, ORDER_DATE
FROM ORDERS
WHERE TO_CHAR(ORDER_DATE, 'DD-MMM-YYYY') = '07-JUN-2000';

try to_char(order_date, 'DD-MON-YYYY')

Related

Unable to get data between two years

I am not getting data between two years, below is between condition
to_char(Wfc.APPLYDTM,'MM/DD/YYYY') between '12/11/2019' and '01/10/2020'
but I am getting data between '12/11/2019' and '12/31/2019' & '01/11/2020' and '01/01/2020' for these dates but not between two different years.
Please help
Try using TO_DATE instead of TO_CHAR, and then compare against valid Oracle date literals:
SELECT *
FROM Wfc
WHERE TO_DATE(APPLYDTM, 'MM/DD/YYYY') BETWEEN date '2019-12-11' AND date '2019-01-10';
Note that if APPLYDTM already be a date, then you don't need to call TO_DATE on it. It doesn't make sense to convert your data to character, if you intend to work with it as a date.
You should convert your data to Date to be able to compare correctly.
The main idea is you should compare date value instead of string value.
to_date(Wfc.APPLYDTM,'MM/dd/yyyy') between to_date('12/11/2019','MM/dd/yyyy') and to_date('01/10/2020','MM/dd/yyyy')
Read here to more details.
Do not convert date/time values to strings! Use the built in functionality.
Your logic is most simply expressed as:
Wfc.APPLYDTMbetween >= DATE '2019-12-11' AND
Wfc.APPLYDTMbetween < DATE '2020-01-11'
Note that the date constants are provided using the DATE keyword. This supposed ISO 8601 standard date formats (happily!).
Also note the use of >= and < rather than BETWEEN. The date data type in Oracle can include a time component -- even if you don't see it when you query the table. This ensures that all date/times are included in the range.
As an added benefit, this can use an index on (APPLYDTMbetween). Using a function usually precludes using an index, unless you have defined a function-based index.

SQL Date Format Conversion

I have a question regarding SQL dates.
The table I am working with has a date field in the following format: "22-SEP-08". The field is a date column.
I am trying to figure out how to output records from 1/1/2000 to present day.
The code below is not filtering the date field:
Select distinct entity.lt_date
from feed.entitytable entity
where entity.lt_date >= '2000-01-01'
Any help regarding this issue is much appreciated. Thanks!
Edit: I am using Oracle SQL Developer to write my code.
DATEs do not have "a format". Any format you see is applied by the application displaying the date value.
You can either change the configuration of SQL Developer to display dates in a different format, or you can use to_char() to format the date the way you want.
The reason your statement does not work, is most probably because of the implicit data type conversion that you are relying on.
'2000-01-01' is a string value, not a date. And the string is converted using the NLS settings of your session. Given the fact that you see dates displayed as DD-MON-YY means that that is the format that is used by the evil implicit data type conversion. You should supply date values always as real date literals.
There are two ways of specifying a real date literal. The first is ANSI SQL and simple uses the keyword DATE in front of an ISO formatted string:
where entity.lt_date >= DATE '2000-01-01'
Note the DATE keyword in front of the string, wich makes it a real date literal not a string expression.
The other option is to use to_date() to convert a character value into a date:
where entity.lt_date >= to_date('2000-01-01', 'yyyy-mm-dd');
More details about specifying date literals can be found in the manual:
Date literals
to_date function
My guess is the data type isn't a Date. Just in case its a char type, try to convert it using the Oracle TO_DATE() function. The Oracle documentation below should help you with parameters.
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions183.htm
An implicit datatype conversion bites once again.
You're right. The predicate is not doing the comparison you are expecting,
Oracle is performing an implicit datatype conversion, from DATE to VARCHAR, so that it can do a comparison to the string literal.
If lt_date column is DATE datatype, then Oracle is seeing your where clause:
where entity.lt_date >= '2000-01-01'
Oracle is actually seeing it as if it's written like this:
where TO_CHAR(entity.lt_date) >= '2000-01-01'
And that's where the "format" problem comes in. The column itself does not have a "format". Because the second argument to the TO_CHAR function is not supplied, Oracle is using the value of the NLS_DATE_FORMAT parameter (from your session). And that's probably set to DD-MON-YY. Which is why that's the "format" you're seeing when you a run a SELECT statement in SQL*Plus. Because the DATE value is (again) being run through a TO_CHAR function to get a string that can be displayed.
To get the "filtering" you want, don't do a comparison to a string literal. Instead, do the comparison to an expression that has DATE datatype.
You can use the Oracle TO_DATE function. And you don't want to rely on setting of NLS_DATE_FORMAT, explicitly specify the format model as the second argument to the function. For example:
DO THIS
where entity.lt_date >= TO_DATE('2000-01-01','YYYY-MM-DD')
DON'T DO THIS
It's also possible to specify the format model as the second argument to the TO_CHAR function.
where TO_CHAR(entity.lt_date,'YYYY-MM-DD') >= '2001-01-01'
But you don't want to do that because that's going to force Oracle to evaluate that expression on the left side for every flipping row in the table, so it has a string value to do the comparison. (That's true unless someone created a function-based index for you.) If you do the comparison on the bare column, using the TO_DATE on the literal side, Oracle can make effective use of an appropriate index (with lt_date as the leading column) to satisfy the predicate.

Is this query correct? SELECT s FROM Salesmen s WHERE s.dateOfEmployment < '26-06-2012' ORDER BY s.salepersonId ASC

I am not sure how to query for a date and I have tried it without the ' ' but it does not work. I was wonder if this is correct. Dateofemployment is a Date in the database and the date I put there is the date.
SELECT s
FROM Salesmen s
WHERE s.dateOfEmployment < '26-06-2012'
ORDER BY s.salepersonId ASC
Never assume there's a specific date format (dmy,ymd,mdy,etc.), always use the functions of your DBMS for writing dates instead of writing a string literal, e.g. TO_DATE in Oracle (and some others).
In best case you can use Standard SQL's date literals:
WHERE s.dateOfEmployment < DATE '2012-06-26'
Most DBMSes support it and there's no ambiguity because there's only one format allowed: yyyy-mm-dd
Your query is correct and you must put the two quotes since dateOfEmployment is a date. However, be careful with the position of the day and the month and make sure that your query does what you want.
When you leave out the quotes, you have a simple arithmetic expression: 26 - 06 - 2012. This is equal to something like -1992. That is an integer and not a date, so presumably the comparison will either generate an error or at least never return true.
When putting dates in queries, you should use ISO standard format: YYYY-MM-DD. Most databases will accept the following:
WHERE s.dateOfEmployment < '2012-06-26'
In Oracle, you need to add date before the constant.

Find record between two dates

When I write below query it gives record .
SELECT [srno],[order_no],[order_date],[supplier_name],[item_code],[item_name],[quntity]
FROM [first].[dbo].[Purchase_Order]
WHERE order_date BETWEEN '22/04/2015' AND '4/05/2015'
In this query if I don't add 0 in '4/05/2015' it returns record.
But when I add 0 to the date i.e. '04/05/2015' it doesn't give any records.
SELECT [srno],[order_no],[order_date],[supplier_name],[item_code],[item_name],[quntity]
FROM [first].[dbo].[Purchase_Order]
WHERE order_date BETWEEN '22/04/2015' AND '04/05/2015'
The reason it's not working because SQL is trying to do a string comparison because both your types are string types, But what you really want to do a date comparison.
You should do something like this. Since you only need date part you can strip off the time and use style 103 for your format dd/mm/yyyy.
WHERE CONVERT(DATETIME,LEFT(order_date,10),103)
BETWEEN CONVERT(DATETIME,'20150422') AND CONVERT(DATETIME,'20150504')
Alternately you can use this as well if your order_date has dates like this 5/4/2015 03:20:24PM
WHERE CONVERT(DATETIME,LEFT(order_Date,CHARINDEX(' ', order_Date) - 1),103)
BETWEEN CONVERT(DATETIME,'20150422') AND CONVERT(DATETIME,'20150504')
A long term solution is to change your column order_date to DATE/DATETIME
It Better to Cast it to date rather than depend on IMPLICIT conversion
SELECT [srno],[order_no],[order_date],[supplier_name],[item_code],
[item_name],[quntity] FROM [first].[dbo].[Purchase_Order] where
convert(date,order_date,105) BETWEEN cast('22/04/2015' as Date) AND cast('04/05/2015' as date)

Equals(=) vs. LIKE for date data type

First, I am aware that this question has been posted generally Equals(=) vs. LIKE.
Here, I query about date type data on ORACLE database, I found the following, when I write select statment in this way:
SELECT ACCOUNT.ACCOUNT_ID, ACCOUNT.LAST_TRANSACTION_DATE
FROM ACCOUNT
WHERE ACCOUNT.LAST_TRANSACTION_DATE LIKE '30-JUL-07';
I get all rows I'm looking for. but when I use the sign equal = instead :
SELECT ACCOUNT.ACCOUNT_ID, ACCOUNT.LAST_TRANSACTION_DATE
FROM ACCOUNT
WHERE ACCOUNT.LAST_TRANSACTION_DATE = '30-JUL-07';
I get nothing even though nothing is different except the equal sign. Can I find any explanation for this please ?
Assuming LAST_TRANSACTION_DATE is a DATE column (or TIMESTAMP) then both version are very bad practice.
In both cases the DATE column will implicitly be converted to a character literal based on the current NLS settings. That means with different clients you will get different results.
When using date literals always use to_date() with(!) a format mask or use an ANSI date literal. That way you compare dates with dates not strings with strings. So for the equal comparison you should use:
LAST_TRANSACTION_DATE = to_date('30-JUL-07', 'dd-mon-yy')
Note that using 'MON' can still lead to errors with different NLS settings ('DEC' vs. 'DEZ' or 'MAR' vs. 'MRZ'). It is much less error prone using month numbers (and four digit years):
LAST_TRANSACTION_DATE = to_date('30-07-2007', 'dd-mm-yyyy')
or using an ANSI date literal
LAST_TRANSACTION_DATE = DATE '2007-07-30'
Now the reason why the above query is very likely to return nothing is that in Oracle DATE columns include the time as well. The above date literals implicitly contain the time 00:00. If the time in the table is different (e.g. 19:54) then of course the dates are not equal.
To workaround this problem you have different options:
use trunc() on the table column to "normalize" the time to 00:00
trunc(LAST_TRANSACTION_DATE) = DATE '2007-07-30
this will however prevent the usage of an index defined on LAST_TRANSACTION_DATE
use between
LAST_TRANSACTION_DATE between to_date('2007-07-30 00:00:00', 'yyyy-mm-dd hh24:mi:ss') and to_date('2007-07-30 23:59:59', 'yyyy-mm-dd hh24:mi:ss')
The performance problem of the first solution could be worked around by creating an index on trunc(LAST_TRANSACTION_DATE) which could be used by that expression. But the expression LAST_TRANSACTION_DATE = '30-JUL-07' prevents an index usage as well because internally it's processed as to_char(LAST_TRANSACTION_DATE) = '30-JUL-07'
The important things to remember:
Never, ever rely on implicit data type conversion. It will give you problems at some point. Always compare the correct data types
Oracle DATE columns always contain a time which is part of the comparison rules.
You should not compare a date to a string directly. You rely on implicit conversions, the rules of which are difficult to remember.
Furthermore, your choice of date format is not optimal: years have four digits (Y2K bug?), and not all languages have the seventh month of the year named JUL. You should use something like YYYY/MM/DD.
Finally, dates in Oracle are points in time precise to the second. All dates have a time component, even if it is 00:00:00. When you use the = operator, Oracle will compare the date and time for dates.
Here's a test case reproducing the behaviour you described:
SQL> create table test_date (d date);
Table created
SQL> alter session set nls_date_format = 'DD-MON-RR';
Session altered
SQL> insert into test_date values
2 (to_date ('2007/07/30 11:50:00', 'yyyy/mm/dd hh24:mi:ss'));
1 row inserted
SQL> select * from test_date where d = '30-JUL-07';
D
-----------
SQL> select * from test_date where d like '30-JUL-07';
D
-----------
30/07/2007
When you use the = operator, Oracle will convert the constant string 30-JUL-07 to a date and compare the value with the column, like this:
SQL> select * from test_date where d = to_date('30-JUL-07', 'DD-MON-RR');
D
-----------
When you use the LIKE operator, Oracle will convert the column to a string and compare it to the right-hand side, which is equivalent to:
SQL> select * from test_date where to_char(d, 'DD-MON-RR') like '30-JUL-07';
D
-----------
30/07/2007
Always compare dates to dates and strings to strings. Related question:
How to correctly handle dates in queries constraints
The date field is not a string. Internally an implicit conversion is made to a string when you use =, which does not match anything because your string does not have the required amount of precision.
I'd have a guess that the LIKE statement behaves somewhat differently with a date field, causing implicit wildcards to be used in the comparison that eliminates the requirement for any precision. Essentially, your LIKE works like this:
SELECT ACCOUNT.ACCOUNT_ID, ACCOUNT.LAST_TRANSACTION_DATE
FROM ACCOUNT
WHERE ACCOUNT.LAST_TRANSACTION_DATE BETWEEN DATE('30-JUL-07 00:00:00.00000+00:00') AND DATE('30-JUL-07 23:59:59.99999+00:00');