Using 'IN' to compare Date - Oracle - sql

I have a table that I need to filter based on date. For my problem, I have to filter the record which are relevant only for the current date without the time portion of the date.
For that I have used the following approach. Query seems to be working fine but I would like to know whether there are any pitfalls in this approach.Part of the WHERE clause related to the query is as follows.
AND TO_CHAR(EOD_DATE,'YYYY-MM-DD') IN TO_CHAR(sysdate,'YYYY-MM-DD')
EOD_DATE is the field in the table I take into consideration.I want records that EOD_DATE value is in the current date regardless of the time portion of EOD_DATE.
Thanks in advance :)

DATE datatype is up to a second precise. Therefore:
SQL> select trunc(sysdate) d_from, trunc(sysdate + 1) - 1/(24*60*60) d_to from dual;
D_FROM D_TO
------------------- -------------------
21.06.2018 00:00:00 21.06.2018 23:59:59
says that - if you want to be able to use index on the EOD_DATE column - you should consider
where eod_date between trunc(sysdate)
and trunc(sysdate + 1) - 1/(24*60*60)

Your solution works, but you are using a double conversion to compare strings instead of comparing dates. Besides, you don't need IN but an =
This could be a better, more readable way:
... and trunc(sysdate) = trunc(EOD_DATE)

Simply use TRUNC to remove the time component (or rather set it to 00:00:00) and then compare the dates:
WHERE TRUNC(EOD_DATE) = TRUNC(SYSDATE)

Related

How to transform Oracle DateTime to Date

For instance, I have a datetime like this '2016-04-02 00:00:00' and another like this '2016-04-02 15:10:00'. I don't care about the time-part, I want them to match just by the date-part.
I have tried with date(), to_date, datepart, nothing works.
Do it like this:
where yourField >= the start of your date range
and yourField < the day after the end of your date range
Edit starts here:
While you could use trunc, as suggested by others, bear in mind that filtering on function results tends to be slow.
Truncating the date to day should do the trick. Documentation here:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions201.htm
For example
SELECT TRUNC(SYSDATE, 'DAY') FROM DUAL;
As others have said - there is no separate "date" data type in Oracle. A pure "date" is stored as a date with the time portion set to 00:00:00 (midnight at the beginning of the day), and TRUNC(date) will take any date and truncate the time to 00:00:00, so if you need to compare two dates you can write
where trunc(date_1) = trunc(date_2)
If your tables are very large and you need to do these comparisons often, this is not ideal, because wrapping column values within function calls (like date_1 within a TRUNC) prevents the use of an index you may have on the date_1 column. If you need to compare dates in two columns you may not have much of a choice, but if you compare to a fixed date (or something like SYSDATE) you may be better off with something like
where date_1 >= trunc(sysdate) and date_1 < trunc(sysdate) + 1
Here you are not using trunc on the column value, so if there's an index on the column, Oracle is free to use it - and trunc(sysdate) is computed only once, not for every single row. "+1" by the way means "add one day".
TO_DATE converts a string to a date; if you apply TO_DATE to a value that is already a legitimate date, you will get unexpected results because Oracle will first convert your true date to a string and then back to date again, and since these conversions require a date FORMAT for strings, and the formats Oracle assumes for conversion from date to string and from string to date may not match, .... you get the idea. As far as I know, DATE() (a FUNCTION) and DATEPART do not exist in Oracle; when you use a new language, keep Google close by and use it often.
If you input a date with no time component, for example TO_DATE('04-apr-2016, 'dd-mon-yyyy'), then the implicit time is 00:00:00 so you don't need to apply TRUNC() to it.
Good luck!

Sysdate Oracle to Run Based on Day

This is probably an easy question for most of you but how can I get this mask to run based on just the day?
If anyone knows Crystal Reports syntax, we have this and it works {PO_RECEIPTS.DATE_RECEIVED} = currentdate
However, when converting to Oracle SQL, how can I the standard: TO_CHAR
(SYSDATE, 'MM-DD-YYYY HH24:MI:SS') to become range so we can selected everything during the day, not just what matched the second in which the report was ran which it never will.
So something like Today from 00:00:00 to 23:59:59 ?
Thank you!
If PO_RECEIPTS.DATE_RECEIVED is a date column where all the times are set to midnight then you can do:
WHERE PO_RECEIPTS.DATE_RECEIVED = TRUNC(sysdate)
If the values have other times then you can use a range:
WHERE PO_RECEIPTS.DATE_RECEIVED >= TRUNC(sysdate)
AND PO_RECEIPTS.DATE_RECEIVED < TRUNC(sysdate) + 1
Truncating a date sets the time to midnight, by default, so TRUNC(sysdate) is midnight this morning. For the range you get all records equal to or later than midnight this morning, and less than midnight tomorrow - which is what TRUNC(sysdate) + 1 gives you, using normal Oracle datetime arithmetic.
You don't really want to convert it to a string with TO_CHAR(); you'd either have to convert all the column values to strings too (which is inefficient and prevents an index being used), or let the string be (implicitly) converted back to a date anyway. It's better to compare a column value with the same data type to reduce or avoid confusion.

Oracle SQL - Determine if a timestamp falls within a range, excluding date

I'm looking for a way to determine if a timestamp falls between two times, regardless of the date in that timestamp. So for example, if the time in the timestamp falls between '00:00:00.000' (midnight) and '01:00:00.000' (1 A.M.), I'd want to select that row regardless of the particular date.
I've tried lots of different variations on the to_char and to_date functions, but I keep getting errors. Coming from Informix, Oracle seems much more complicated.
The thing closest to "correct" (I think) that I've tried is:
SELECT *
FROM my_table
WHERE SUBSTR(TO_CHAR(my_timestamp), 10) > '00:00:00.000'
AND SUBSTR(TO_CHAR(my_timestamp), 10) < '01:00:00.000'
... But nothing works. Any tips or tricks?
I found a way to do it, but I'd still prefer something a little less hacky, if it exists.
SUBSTR(SUBSTR(TO_CHAR(my_timestamp), 11), 0, 12) > '01.00.00.000'
Your solution looks correct to me except I haven't tried substr function. This is what I used in one of my previous project:
select * from orders
where to_char(my_timestamp,'hh24:mi:ss.FF3')
between '00:00:00.000' and '01:00:00.123';
Use TRUNC(my_timestamp, 'J') to remove the hours and get only the '2013-08-15 00:00:00.00'.
So:
WHERE my_timestamp - TRUNC(my_timestamp, 'J') > 0
AND my_timestamp - TRUNC(my_timestamp, 'J') < 1/24 ;
As a variation on #kubanczyk's answer, since these are timestamps you get an interval when you subtract a value from its truncated form:
select systimestamp - trunc(systimestamp) from dual;
SYSTIMESTAMP-TRUNC(SYSTIMESTAMP)
---------------------------------------------------------------------------
+000000000 09:46:46.589795
Which isn't very helpful. But if you're always looking for exact hours, as in your example, you can extract the hour number from that:
select extract (hour from systimestamp - trunc(systimestamp)) from dual;
EXTRACT(HOURFROMSYSTIMESTAMP-TRUNC(SYSTIMESTAMP))
-------------------------------------------------
9
So in your example you could use:
SELECT *
FROM my_table
WHERE EXTRACT(HOUR FROM my_timestamp - TRUNC(my_timestamp)) = 0
SQL Fiddle demo.
But, this will only be straightforward if the timeslots are exactly aligned with hours; otherwise you'd need to extract other elements too and the logic could get confusing, and #Ankit's approach will be simpler overall.

Issue with date formatting SQL

SELECT LISTING_EOD.LOCATION, LISTING_EOD.APPTTIME, LISTING_EOD.PERSON_ID,
LISTING_EOD.FORENAME, LISTING_EOD.SURNAME, LISTING_EODS.STATUS,
LISTING_EOD.DBDATE
FROM DBNAME.LISTING_EOD LISTING_EOD;
This query returns a list of data processed today, I need to modify to check yesterday's data. I have tried add the below line of code, but it doesn't return anything. Does anyone know how I can achieve this?
where LISTING_EOD.DBDATE = '18-OCT-2012';
If you always want yesterday's data, rather than hard-coding the date you can use:
WHERE LISTING_EOD.DBDATE >= TRUNC(SYSDATE) - 1
AND LISTING_EOD.DBDATE < TRUNC(SYSDATE)
TRUNC(SYSDATE) gives you midnight this morning, so if run today it would give a range between 18-Oct-2010 00:00:00 and 18-Oct-2012 23:59:59.
It's generally not a good idea to use implicit date format masks; your original code assumes your NLS_DATE_FORMAT is set to DD-MON-YYYY, but that might not be correct now (if you're seeing the time in the existing select then it probably isn't), and may well not be in the future. Always use an explicit date format mask, like TO_DATE('18-OCT-2012', 'DD-MON-YYY'), to avoid ambiguity and unexpected behaviour.
If the field is actually VARCHAR2 rather than a DATE - which is bad - then you'll need to convert the date range to a string to get a match:
WHERE LISTING_EOD.DBDATE >= TO_CHAR(TRUNC(SYSDATE) - INTERVAL '1' DAY, 'DD-MON-YYYY HH24:MI:SS')
AND LISTING_EOD.DBDATE <= TO_CHAR(TRUNC(SYSDATE) - INTERVAL '1' SECOND, 'DD-MON-YYYY HH24:MI:SS')
That will work for a single day, just, but you'd have problems looking for a date range. It's much better and safer to store data in a column of the appropriate type.
Dates in Oracle by default contain time as well. If you just specify '18-OCT-2012', it will only match 18-OCT-2012 00:00:00'. One way to get around this is to format your database date to what you are comparing it to, e.g. to_char(LISTING_EOD.DBDATE, 'DD-MON-YYYY') and compare this to '18-OCT-2012'. This comparison will disregard time completely.
If you had a date variable to compare with instead of a string, format this using the same date mask used for the database date. This also gets around any assumptions abut default date format on the database in question.
I realised the 'table' I was querying was a view, examined it inside sqldeveloper, and added '-1' to the sysdate. This query then returned the previous days results.
I successfully retrieved the correct data, thanks for all help received.

how to use = assignment operator with timestamp date column in oracle

I'm using timestamp in dat column in table r3. when I fire command
select dat from r3 where dat='16-nov-09';
it shows "no rows selected" but when i fire command
select dat from r3 where dat>'15-nov-09';
it shows the whole data of 16-nov-09. Tell me what is wrong in my first command or what i have to do.
Quering on oracle date columns is always confusing. The date columntype is always a datetime. Storing the current date from sysdate stores always the time component too.
There good and evil ways quering the date columns. I show and vote some.
where to_char(DAT, 'DD-MON-YYYY') = '16-NOV-2009'
where trunc(DAT) = to_date('16-NOV-2009', 'DD-MON-YYYY')
Both bad, because they do not use any index. To avoid this, you can define a function based index on the expression.
The trick of both is to cut off the time component. If time is not needed, than it is a good advise to cut off the time in INSERT and UPDATE trigger. The function based index can convert to a normal index.
where DAT between to_date('16-NOV-2009', 'DD-MON-YYYY')
and to_date('16-NOV-2009 23:59:59', 'DD-MON-YYYY HH24:MI:SS')
where DAT >= to_date('16-NOV-2009', 'DD-MON-YYYY') and DAT < to_date('16-NOV-2009', 'DD-MON-YYYY')+1
This two are always my favorites.
Its a good advice to use to_date and to_char to convert the values between string and datetime.
As DAT is timestamp you can use as below
select DAT from R3
where DAT between to_date('16-NOV-09' , 'dd-MON-yy') and to_date('16-NOV-09 23:59:59', 'DD-MON-YY hh24:mi:ss')
Timestamp has time and date components, so query
select dat from r3 where dat='16-nov-09';
will work only for records where time component is midnight: '00:00:00'
Beside formatting (to_date function), you can truncate timestamp to get only date:
select dat from r3 where trunc(dat)='16-nov-09';
Beware that this will not use index on field dat (if there is any).
TIMESTAMP and DATE are different data types in oracle and both store time components. If you really do need to store subsecond times then you use TIMESTAMP, otherwise DATE is your best choice.
The ANSI timestamp and date literal syntaxes are quite handy:
create table ts_test (ts1 timestamp);
select *
from ts_test
where ts1 > timestamp '2009-10-11 00:00:00'
/
select *
from ts_test
where ts1 > timestamp '2009-10-11 00:00:00.1'
/
select *
from ts_test
where ts1 > timestamp '2009-10-11 00:00:00.001'
/
select *
from ts_test
where ts1 = date '2009-10-11'
/
use the below format for a date field in where condition.
where to_char(DAT,'mmddyyyy') = '11152009';
In Oracle the date fields also contain a time component, so 16-nov-09 is actually midnight of Nov 16th.
Two different ways to handle this:
where to_char(DAT,'mmddyyyy') = '11152009'
as john suggested, but I like the following version more:
where trunc(dat) = to_date ('11152009', 'mmddyyyy')
TRUNCfor a date "removes" the time component (or to be more specific, truncates it to midnight), and to_date is the proper way to construct a date value in Oracle SQL. (I prefer to do the comparisons in the right domain - DATEs as in the second example- over another - STRINGs as in the first example. With strings you may run into some weird month issues, sorting is easier in dates etc.)
Just to add to it , An easy way out when you are not bothered about the time-stamp but just want to compare the date is to use the 'like' operator.
for example
select dat from r3 where dat LIKE '16-nov-09%'
will give you desired output.