Date difference = 0 in where clause Oracle? - sql

I have the same problem as this. The only difference is that I use Oracle. I want to select the rows which has insertion_date='20.11.2018'. So my query was
select * from table where insertion_date='20.11.2018'
In that question they suggested datediff, so I looked at its equivalent in oracle and I learned that I can do date arithmetic. So I tried somethings like these:
select * from table where insertion_date -'20.11.2018'=0;
It gave ora-00932 inconsistent datatypes expected date got number.
So, then I tried;
select * from table where insertion_date - to_date('20.11.2018', 'dd.MM.YYYY') = 0;
It does not give error but also does not display the results which I know there must be. What am I doing wrong here? Thanks.
Update: Sorry I forgot to mention that insertion_date is type date. But it also has time(hour, minutes, seconds) info in it.

What is INSERTION_DATE's datatype?
If it is DATE, then comparing it to another date (note: this is date literal; value you used is a string!)
select * from table where insertion_date = date '2018-11-20'
might work, unless INSERTION_DATE contains time component (hours and minutes). Then, the simplest option is to truncate its value (so that you get date itself, at midnight):
select * from table where trunc(insertion_date) = date '2018-11-20'
but it'll ruin index you have on that column (unless it is a function-based one). For small tables, it won't make any difference. For large amount of data, it would so convert it to
select * from table where insertion_date >= date '2018-11-20'
and insertion_date < date '2018-11-21'
If, on the other hand, INSERTION_DATE is a string (VARCHAR2 or CHAR) datatype (which is a really bad idea; consider switching to DATE datatype), then you have to know its format, convert it to DATE first and then compare to another date. For example, if it was a string that contains date values in format dd.mm.yyyy, then
select * from table where to_date(insertion_date, 'dd.mm.yyyy') = date '2018-11-20'
This will certainly fail if any string in that column doesn't match such a format or contains invalid values (such as "date" 53.67.Bx48).

Related

SQL Oracle table blank when trying to query date data stored as varchar

I have a column called received_dt_key in Varchar in the format DD-MM-YYYY (e.g. 30-07-2021).
I would like to select all from the table for dates between 31-12-2021 and 01-01-2022. I have tried version of the below query and a blank table is the output.
SELECT *
FROM SD_BDAY
WHERE to_char(to_date(RECEIVED_DT_KEY, 'DD-MM-YYYY')) > to_char(to_date('31-12-2021', 'DD-MM-YYYY'))
and to_char(to_date(RECEIVED_DT_KEY, 'DD-MM-YYYY')) < to_char(to_date('01-01-2022', 'DD-MM-YYYY'));
Don't compare dates as strings. Compare them as dates:
SELECT *
FROM SD_BDAY
WHERE to_date(RECEIVED_DT_KEY, 'DD-MM-YYYY') > to_date('31-12-2021', 'DD-MM-YYYY')
and to_date(RECEIVED_DT_KEY, 'DD-MM-YYYY') < to_date('01-01-2022', 'DD-MM-YYYY');
If you try to compare them as strings then you are looking for string that is greater than '31-12-2021' and less than '01-01-2022' and the string comparison will look at the first character and see if it can find a match which is greater than '3' and less than '0'; there can never be such a match so it is quite correct that when comparing as strings nothing is returned.
As pointed out by #AlexPoole in comments, even if you compare the values as dates (rather than strings) you will still never return a result as finding values that are greater than DATE '2021-12-31' and less than DATE '2022-01-01' would return all dates from 2021-12-31 00:00:01 to 2021-12-31 23:59:59; however, your values will always be converted with a midnight time component and, therefore, will never fall into that range so cannot be returned.
What you probably want is to use >= rather than > and then it would match values on 2021-12-31.
The best thing would be to store calendar dates in date data type column. Why else do you think Oracle designed that data type? This way you may create normal indexes on data data type columns, or, if needed, partition the table by that date column.
Still, if you insist in having the calendar dates stored like that, I think the below should work:
SELECT *
FROM SD_BDAY
WHERE to_date(RECEIVED_DT_KEY, 'DD-MM-YYYY') >
to_date('31-12-2021', 'DD-MM-YYYY')
and to_date(RECEIVED_DT_KEY, 'DD-MM-YYYY') <
to_date('01-01-2022', 'DD-MM-YYYY');
Thus you compare calandar dates with calendar dates, not varchar with varchar, as it results from the code you have written.
And what if in the varchar2 column there is somethibng that can't be converted to date? That is why it is best to use the date data type.

SELECT matching dates from a timestamp with time zone column

I have a timestamp with time zone column within which I'd like to run a query returning all matching dates. eg. I want all rows which have a timestamp with date 2019-09-30. I'm trying something like this but haven't been able to figure it out:
SELECT * FROM table WHERE
x='1277' AND
date='2019-09-30 21:40:01.316240 +00:00'::DATE;
There are two options:
range search:
WHERE timestampcol >= TIMESTAMPTZ '2019-09-30'
AND timestampcol < (TIMESTAMPTZ '2019-09-30' + INTERVAL '1 day')
The proper index to make this fast is
CREATE INDEX ON atable (timestampcol);
conversion to date:
WHERE CAST(timestampcol AS date) = '2019-09-30'
The proper index to make this fast is
CREATE INDEX ON atable ((CAST(timestampcol AS date)));
Both methods work equally well. The second method has a shorter WHERE clause, but a specialized index that maybe no other query can benefit from.
You can use such a collation among your date converted column value and fixed date value :
with tab( x, date ) as
(
select 1277, '2019-09-30 21:40:01.316240 +00:00'::timestamp with time zone union all
select 1278, '2019-09-29 21:40:01.316240 +00:00'::timestamp with time zone
)
select *
from tab
where date::date = date'2019-09-30';
Demo
I am not familiar with postgresql, but the following page https://www.postgresql.org/docs/9.1/datatype-datetime.html indicates that there is an optional precision value p that is used to specify the number of fractional digits retained in the seconds field. How was the data type configured for your date field? Is the timestamp column being stored as an eight byte integer or a floating-point number? If the later, the effective limit of precision might be less than 6. All of this info is on the above linked page.
You have several options, as they have been mentioned, and may vary depending on the data type of your field named "date".
For example,
Turn your field to date, returning format 'yyyy-mm-dd':
SELECT * FROM table WHERE x='1277' AND date::DATE='2019-09-30';
"date" just after field name.
Convert it to char and retrieve 10 characters:
SELECT * FROM table WHERE x='1277' AND LEFT(date::varchar,10)='2019-09-30';
Like the previous:
SELECT * FROM table WHERE x='1277' AND to_char(date,'yyyymmdd')='20190930';
And there are many others. For more specific info, you have to check PostgreSQL documentation to check which one is best for you or post more information in order we can guess more about your problem.
https://www.postgresql.org/docs/9.4/functions-datetime.html

Strange behaviour of Sql query with between operator

There is this strange error in sql query.
The query is something like this.
select * from student where dob between '20150820' and '20150828'
But in the database the column of dob is varchar(14) and is in yyyyMMddhhmmss format,Say my data in the row is (20150827142545).If i fire the above query it should not retrive any rows as i have mentioned yyyyMMdd format in the query.But it retrives the row with yesterday date (i.e 20150827112535) and it cannot get the records with today's date (i.e 20150828144532)
Why is this happening??
Thanks for the help in advance
You can try like this:
select * from student
where convert(date,LEFT(dob,8)) between
convert(date'20150820') and convert(date,'20150828'))
Also as others have commented you need to store your date as Date instead of varchar to avoid such problems in future.
As already mentioned you would need to use the correct date type to have between behave properly.
select *
from student
where convert(date,LEFT(dob,8)) between '20150820' and '20150828'
Sidenote: You don't have to explicitly convert your two dates from text as this will be done implicitly as long as you use an unambiguous date representation, i.e. the ISO standard 'YYYYMMDD' or 'YYYY-MM-DD'. Of course if you're holding the values in variables then use date | datetime datatype
declare #startdate date
declare #enddate date
select *
from student
where convert(date,LEFT(dob,8)) between #startdate and #enddate
Sidenote 2: Performing the functions on your table dob column would prevent any indexes on that column from being used to their full potential in your execution plan and may result in slower execution, if you can, define the correct data type for the table dob column or use a persistent computed column or materialised view if your performance is a real issue.
Sidenote 3: If you need to maintain the time portion in your data i.e. date and time of birth, use the following to ensure all records are captured;
select *
from student
where
convert(date,LEFT(dob,8)) >= '20150820'
and convert(date,LEFT(dob,8)) < dateadd(d,1,'20150828')
All you have to do is to convert first the string to date.
select *
from student
where dob between convert(date, '20150820') and convert(date, '20150828')
Why is this happening?
The comparison is executed from left to right and the order of characters is determined by the codepage in use.
Sort Order
Sort order specifies the way that data values are sorted, affecting
the results of data comparison. The sorting of data is accomplished
through collations, and it can be optimized using indexes.
https://msdn.microsoft.com/en-us/library/ms143726.aspx
There are problems with between in T-SQL.
But if you want a fast answer convert to date first and use >= <= or even datediff to compare - maybe write a between function yourself if you want the easy use like between and no care about begin and start times ...
What do BETWEEN and the devil have in common?

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');

Between operation for date in SQLite database

I have a table student with the following columns:
no - integer
name - string
startdate - date
enddate - date.
Date format is MM/DD/YYYY.
I will give a date as input. Now I need a query the inputdate which found in between the start and end date.
For an example I will give 04/14/2012, then the query should return the 1st record as in the figure.
(because input date (04/14/2012) is found in between the 04/10/2012 to 04/20/2012)
Please help me.
The issue you are having is caused by your assumption that sqlite has a date/datetime type when in fact it doesn't.
I suggest you read the following http://www.sqlite.org/datatype3.html to have a better understanding of sqlite types.
The dates in the MM/DD/YYYY format are handled as TEXT by sqlite, and so those dates are compared as strings. For example, 02/01/2012 is considered bigger than 01/02/2012by sqlite if compared directly.
You will need to transform those dates to a format that can be string-compared. Here is an example:
sqlite> create table foo (d TEXT);
sqlite> insert into foo values ('02/01/2012');
sqlite> select substr(d, 7, 4) || substr(d, 1, 2) || substr(d, 4, 2) from foo;
20120201
You should post what you have tried so far.
There should be a between clause that you can use:
select * from table
where inputdate between startdate and enddate
Dates as a date type in SQLite don't exist. There are a number of approaches to dealing with dates - store them as integer seconds since 1 Jan 1970 (unixepoch) or store them as strings, but if you do, then you really need to store them in 'YYYY-MM-DD' format because that is what the date functions require as input.
Assuming you use the string format in the format I suggested then your query would look something like
SELECT * FROM Table WHERE Date(Inputdate) BETWEEEN Date(startDate) AND Date(EndDate);
(although you may want to format the output of the date columns to US date format with
SELECT Strftime("%m/%d/%Y",startDate) As StartDate ...
If you use seconds since 1970 its somewhat easier because the seconds just compare without needing the convert them to dates, although you still might want to output in US date format, so ...
SELECT Strftime("%m/%d/%Y",startDate) As StartDate ... FROM Table WHERE inputDate BETWEEN startDate and EndDate;
sqlite> select *from tbl_node where mydate between '2014-02-02' and '2014-02-06';
it show the output :-
1|1|123|456|12eb-ab|1|1|254|123|19|2014-02-03 16:00:44
2|1|123|456|12eb-ab|1|1|254|123|19|2014-02-03 16:01:03
3|1|123|456|12eb-ab|1|1|254|123|19|2014-02-03 16:00:57
4|1|123|456|12eb-ab|1|1|254|123|19|2014-02-03 16:00:34
Here mydate is column name in tbl_node;
we can also use from current time , using now.
sqlite> select *from tbl_node where mydate between '2014-02-02' and 'now';