Reputation: 171
The query below is being used to get a count of records between some days. Actually, there are no records for that particular date so query should return 0. But due to some mistake, it returns the value incorrectly.
SELECT COUNT(*)
FROM transaction_tb
WHERE STATUS NOT IN ('Wrong', 'Dont')
AND to_char(date, 'DD-MM-YYYY') BETWEEN '01-04-2019' AND '31-03-2020';
But when trying with the below query, it returned 0 as excepted.
SELECT COUNT(*)
FROM transaction_tb
WHERE STATUS NOT IN ('Wrong', 'Dont')
AND to_char(date, 'YYYYMMDD') BETWEEN '20190401' AND '20200331';
Both queries should return the same value.
What is the difference between these queries?
Upvotes: 0
Views: 257
Reputation: 415665
When you convert to a character/text type, you're doing a text comparison for the BETWEEN
operation. It's not doing a date check anymore at all. Text comparisons are alphabetical order, character by character, and stop as soon as anything is out of the range.
With that in mind, looking at the first sample:
to_char(date, 'DD-MM-YYYY') BETWEEN '01-04-2019' AND '31-03-2020'
The second character on both boundaries is a 1
, so anything that doesn't have a 1
for the second character will fail. In other words, the only dates that can possibly succeed past this point are the 1st, 11th, 21st, and 31st days of the month.
Going further we get to the 4th character, which is 0
for both boundary literals. Nothing with a 1
can pass this example, including all dates from October through December.
Next up is the 5th character, which is 4
, and 3
, in that order. If we ignore the order issues, which excludes everything, and just think in terms of the 3-4, this excludes any date from January and February, as well as any date after April.
Now let's look at the second example. The one that seems to work:
to_char(date, 'YYYYMMDD') BETWEEN '20190401' AND '20200331';
Applying the same process, we get valid results all the way through the year portion. But then we get to the 0
in 04
and 03
and we have the same problem as before, with not counting October through December. Finally, we also have the same problem on the last character, which limits you to the 1st, 11th, 21st, and 31st days of the month.
What you should do is keep the comparisons as a date
. If you ever find yourself converting to a string type for date check, you're making a big mistake.
Instead, you want to represent your boundaries as date literals:
"date" BETWEEN DATE '2019-04-01' AND DATE '2020-03-31'
Upvotes: 1
Reputation: 2732
Everything in the other answers is good, and very true (always use dates to compare with dates, don't convert to strings).
In addition, when you are only concerned with Y-M-D (and the time-of-day is not important), you should also incorporate TRUNC
which will just remove the H-M-S so that time-of-day does not become an issue:
WHERE TRUNC( TO_DATE( '01-04-2019', 'MM-DD-YYYY' ) ) <= TRUNC( date_column )
AND TRUNC( date_column ) <= TRUNC( TO_DATE( '03-30-2020', 'MM-DD-YYYY' ) )
Upvotes: 2
Reputation: 1269543
Don't use strings for date comparisons!
SELECT COUNT(*)
FROM transaction_tb
WHERE STATUS NOT IN ('Wrong', 'Dont') AND
date >= DATE '2019-01-04' AND
date <= DATE '2020-03-31'
Upvotes: 1
Reputation: 231661
If you compare two dates, you get date comparison semantics. If you compare two strings, you get string comparison semantics which is alphabetical order. The string "10-10-1950" comes alphabetically between the string "01-04-2019" and the string "31-03-2020". Despite the fact that the date it represents is obviously much earlier than either of the dates represented by the other strings.
If you compare dates to dates, you get date comparison semantics which is what you want. Either use date literals
where date_column between date '2019-01-04' and date '2020-03-31'
or use a to_date
to convert your strings to dates
where date_column between to_date( '01-04-2019', 'MM-DD-YYYY' )
AND to_date( '31-03-2020', 'MM-DD-YYYY' )
Upvotes: 3