Subtract one hour from datetime rather than one day - oracle

I have a datetime column in Oracle (MM/DD/YYYY HH:MM:SS AM/PM) but when I do this:
SELECT MAX(D_DTM)-1 FROM tbl1
...it goes back a day. How do I remove one hour from the column rather than one day?
I've also noticed that the datetime records for 12AM look like MM/DD/YYYY and not MM/DD/YYYY 00:00:00; I'm not sure if that matters.

Randy's answer is good, but you can also use intervals:
SELECT MAX(D_DTM)- interval '1' hour FROM tbl1

yes - dates go by integer days.
if you want hours you need to do some math - like -(1/24)

Or use the INTERVAL function. It has the same result but I think it reads more clearly - that's of course just an opinion :)
SELECT MAX(D_DTM) - INTERVAL '1' HOUR FROM tbl1
The nice thing about the INTERVAL function is that you can make the interval be years, months, days, hours, minutes or seconds when dealing with a DATE value, though the month interval can be tricky when dealing with end-of-month dates.
And yes, the quote around the 1 in the example is required.
You can also use the Oracle-specific NumToDSInterval function, which is less standard but more flexible because it accepts variables instead of constants:
SELECT MAX(D_DTM) - NUMTODSINTERVAL(1, 'HOUR') FROM tbl1

select sysdate - numtodsinterval(1,'hour') from dual

Its simple.
sysdate - 5/(24*60*60) --> Subtracts 5 seconds from systime
sysdate - 5/(24*60) --> Subtracts 5 minutes from systime
sysdate - 5/(24) --> Subtracts 5 hours from systime
Hence
select (sysdate - (1/24)) from dual

Another method of using intervals is
NUMTODSINTERVAL( number, expression )
examples
NUMTODSINTERVAL(150, 'DAY')
NUMTODSINTERVAL(1500, 'HOUR')
NUMTODSINTERVAL(15000, 'MINUTE')
NUMTODSINTERVAL(150000, 'SECOND')
I bring this up because it is useful for situations where using INTERVAL wont work.

Related

Convert difference of dates to date with timestamp

How to convert the difference of 2 dates with a timestamp to date with timestamp again, Oracle giving number but i want to compare timestamp.
select emp_date>to_date(sysdate,'yyyy-MM-dd HH24:MI:SS')-todate('2021-03-22 10:20:12') from emp;
above query giving error: expected date but got NUMBER.
Thanks in advance
What you are saying makes no sense. Difference of two DATE datatype values is number of days between them. For example
SQL> select sysdate - to_date('21.03.2021 13:12', 'dd.mm.yyyy hh24:mi') diff from dual;
DIFF
----------
,943217593
SQL>
You CAN convert it to a prettier format (days, hours, minutes, seconds), but it is still a NUMBER, it is not a date.
Therefore, you can't compare EMP_DATE (which is a DATE datatype column, isn't it?) to a number as it just doesn't make sense.
Is 22nd of March 2021 larger or smaller than 0.94? It's neither.
[TL;DR] You cannot as your data types do not match and it does not make sense to compare a date/time value to an interval.
If you do:
date_value1 - date_value2
You will get a NUMBER data type representing the number of (fractional) days between the two date values.
You can explicitly cast the subtraction operation to get an INTERVAL DAY TO SECOND data type using:
(date_value1 - date_value2) DAY TO SECOND
So, for your code that would be:
SELECT emp_date > ( sysdate - TO_DATE( '2021-03-22 10:20:12', 'YYYY-MM-DD HH24:MI:SS' ) ) DAY TO SECOND
FROM emp;
However, that will fail as you cannot compare a DATE to an INTERVAL DAY TO SECOND and SQL does not have a boolean data type so > does not make sense.
To fix that later point you could use a CASE expression but the difference in data types is a show-stopper as you can't compare a date to an interval.
but i want to compare timestamp.
You don't have a TIMESTAMP data type, you have either a number (representing an interval in days) or an INTERVAL data type. If you want to convert it back to a DATE or TIMESTAMP then you need to add your interval to an epoch value.

Create TimeStamp with fixed Time Part

What's the best way to get a timestamp that consists of the actual date but a fixed time part in oracle.
e.g.Today and always 09:00:00
2020-10-20 09:00:00
in MSSQL I would use FORMAT(GETDATE(),'yyyy-MM-dd 09:00:00')
Assuming you want a date rather than a varchar2, I'd use
trunc(sysdate) + interval '9' hour
trunc(sysdate) returns today at midnight and then interval '9' hour adds 9 hours to give you 9am. You can also add fractions of a day to a date so you could say
trunc(sysdate) + 9/24
I tend to find the interval notation more self-explanatory particularly if you're coming from a non-Oracle background.
You can use something like this:
SQL> alter session set NLS_DATE_FORMAT='YYYY-MM-DD';
Session altered.
SQL> set head off
SQL> select sysdate||' 09:00:00' from dual;
2020-10-19 09:00:00
Hope this is what you were looking for :)

Odd result using Oracle trunc()?

Why
select trunc(to_date('23/06/2017','DD/MM/YYYY'), 'DAY') from dual;
returns
19.06.17
instead of expected
23.06.17?
We are on Oracle 11.
The DAY format returns the closest starting day of the week. Depending on your DB configuration, this might be a Sunday, Monday (in your case)...
You probably need the DD format instead.
Oracle doc
DAY truncates to closest SUNDAY [1]
you can use DD.
select trunc(to_date('23/06/2017','DD/MM/YYYY'), 'DD') from dual;
Your format is wrong, should be DD format:
select trunc(to_date('23/06/2017','DD/MM/YYYY'), 'DD') from dual;
Date Format Models for the ROUND and TRUNC Date Functions
DDD
DD
J
Day

months_between for a leap year

When I find months between 28-FEB-11 and 29-FEB-12, months_between function in oracle returns 12. Actually it should be 12.096. This function is not calculating for the leap year proper.
For between 28-FEB-11 and 29-FEB-12, it is 1 year(12 months) and 1 day.
select months_between('28-FEB-12', '28-FEB-11') from dual; -- 12
**select months_between('29-FEB-12', '28-FEB-11') from dual; -- 12**
select months_between('28-FEB-12', '27-FEB-11') from dual; -- 12.0322
select months_between('27-FEB-12', '28-FEB-11') from dual; -- 11.9677
is this an Oracle bug??..
-Vishwa
From the Oracle documentation:
MONTHS_BETWEEN returns number of months between dates date1 and date2. If date1 is later than date2, then the result is positive. If date1 is earlier than date2, then the result is negative. If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer. Otherwise Oracle Database calculates the fractional portion of the result based on a 31-day month and considers the difference in time components date1 and date2.
So it's following the documented behavior. It's just not what you expected.
It's not a bug because ORACLE says so, it's a logical error driven by a human (and documenting as something rigth) which is worse.
How come if difference between the last days of January and February months is exactly 1 month (29 exact days). Please see below:
MONTHS_BETWEEN('29-FEB-12','31-JAN-12')
1
With an extra day (30 days) the difference in months is less than 1. Please see below:
MONTHS_BETWEEN('29-FEB-12','30-JAN-12')
.967741935
WRONG. That's not rigth at all!
SQL Server in another hand handle this correctly:
select DATEDIFF(MM,'29-FEB-12','30-JAN-12')
select DATEDIFF(MM,'29-FEB-12','31-JAN-12')
both are 1

Oracle Interval Bug?

I'm using this sql query:
select sysdate, sysdate - INTERVAL '6' month from dual;
But it is return: ORA-01839: date not valid for month specified.
Which is weird, because if I change the the number into 9, it is return the date (sysdate = 31/05/11 and the subtracted is 31/08/10). I'm also tried using different value: 1,3,6,8,11 also not working, but 2,4,5,7,9,12 are working.
From the numbers, I think it is because the resulting quert doesn't have 31 days for that month. Is this the expected behavior? Because in MySQL, I can use the query (select now() - Interval 6 Month;) to get the correct value. Is there any other way?
I am using Oracle 11.1.0.6
It is the expected behaviour; see the sixth bullet in the datetime/interval arithmetic section of the documentation.
As Lisa says you can use add_months, which has the opposite behaviour - which can also cause confusion sometimes. You need to decide which is most suitable for you.
select sysdate,add_months(sysdate,-6) from dual;

Resources