Sysdate vs Timestamp for comparisons - sql

I'm checking for product updates that are dependent on time of day - alerting if a product has not updated in the last 2 hours , e.g.:
03:30 - 07:29 we expect only product x to be updating;
07:30 - 11:29 we expect both product x and y to be updating
11:30 - 15:30 we expect only product y to be updating
But we need to be sensitive to products that have not updated yesterday and still have not updated today. Thus my question - when doing the below timestamp comparisons for product y, I suspect that date is not factored in and therefore a product that hadn't updated potentially gets ignored.
(...)
AND inv.timestamp < sysdate - (120 / 1440) --older than two hours
AND inv.timestamp > TRUNC(sysdate) + (690/1440) --after 11:30
AND inv.timestamp < TRUNC(sysdate) + (930/1440) --before 15:30
(...)
inv.timestamp is a column in timestamp(6) type. Does trunc(sysdate) implicitly include the present date for comparison or is it only using the time?

TRUNC(SYSDATE) returns 00:00:00 of the current day.
SQL> select trunc(sysdate) from dual;
TRUNC(SYSDATE)
-------------------
2012-03-09 00:00:00
Documentation on TRUNC

As eaolson and Ben have already noted, SYSDATE includes the date AND time. Using TRUNC "removes" the time element, i.e. resets it to midnight of that day.
You may want to consider using the INTERVAL functionality provided by Oracle, to make it clearer what the SQL is doing, e.g.
SELECT to_char(trunc(sysdate) + interval '11' hour + interval '30' minute,'dd/mm/yyyy hh24:mi:ss') sysdate_trunced_to_1130,
to_char(trunc(current_timestamp) + interval '11' hour + interval '30' minute,'dd/mm/yyyy hh24:mi:ss') timestamp_trunced_to_1130
FROM dual;
Glancing at the code, "(690/1440)" doesn't immediately suggest 11:30 in the morning, but + interval '11' hour + interval '30' minute hopefully is a little better.

Related

Oracle Developer - Hour Between 1 and 12 error, works on one machine but not another

I am running into an issue where i have oracle developer on 2 machines, the same code runs successful on one but gives an "hour must be between 1 and 12" error on the other, same versions of Oracle on both.
The code snippet is below. Not sure why this would present differently, I should also note, on the machine it is failing on, is the machine i developed this on a year+ ago, and it worked fine then.
The objective of this item is based on when the script is run, if its after 0400, it runs for a date parameter of todays date >0400 for the time. if its before 0400 when run, it runs for yesterdays date >0400 until.
and cast(scn_time as timestamp) >= case when to_char(current_timestamp, 'HH24:MI:SS')
> ('04:00:00') then to_timestamp(to_char(trunc(sysdate)||' 04.00.00 AM')) else
to_timestamp(to_char(trunc(sysdate-1)||' 4.00.00 AM')) end
Any help would be appreciated.
As #Abra hinted, your two SQL Developer installations have different NLS_TIMESTAMP_FORMAT settings (In Tool->Preferences->Database->NLS). You shouldn't rely on NLS settings, or implicit conversions, as your currently are when you convert the truncated date to a string. You are also relying on your session time zone when you use CURRENT_TIMESTAMP - that could get different values depending on session settings too.
You don't need to convert to or from strings here, you can do:
and scn_time >= case
when extract(hour from current_timestamp) >= 4
then trunc(sysdate) + interval '4' hour
else
then trunc(sysdate) - interval '1' day + interval '4' hour
-- or: then trunc(sysdate) - interval '20' hour
end
or
and scn_time >= trunc(sysdate) + interval '1' hour * case
when extract(hour from current_timestamp) >= 4 then 4
else -20
end
or
and (
(extract(hour from current_timestamp) >= 4
and scn_time >= trunc(sysdate) + interval '4' hour)
or
(extract(hour from current_timestamp) < 4
and scn_time >= trunc(sysdate) - interval '20' hour)
)
This also makes it easier to add an upper limit:
and (
(extract(hour from current_timestamp) >= 4
and scn_time >= trunc(sysdate) + interval '4' hour)
or
(extract(hour from current_timestamp) < 4
and scn_time >= trunc(sysdate) - interval '20' hour)
and scn_time < trunc(sysdate) + interval '4' hour)
)
Casting scn_time to TIMESTAMP probably isn't necessary, and applying a function to a table column will usually prevent any index on it (except an FBI) from being used. If it's already a date then leave it alone and just make the right-hand side of the comparison a date too.
I've left CURRENT_TIMESTAMP in there for now, but you might want SYSTIMESTAMP (which isn't affected by your session settings); though it depends what data type scn_time actually is and what it represents. If that is recording the SCN time for tracking then it might be a timestamp already; in which case cast the right-hand side to the same data type. If it's a string then it shouldn't be, but if you're stuck with it then convert it to a date explicitly with to_date() and the appropriate format mask.

Casting a specific hour and minute and getting the time difference

I have the following:
trunc(sysdate - to_timestamp(trunc(sysdate, 'YYYY-MM-DD') || ' 12:30', 'YYYY-MM-DD HH245:MI') * 1440,1)
What I'm trying to do is, using the date portion of sysdate and casting 12:30 to the time part. Then calculating the difference between sysdate#12:30 and the current sysdate.
I'm getting:
ORA-00932: inconsistent datatypes: expected NUMBER got TIMESTAMP
after the minus sign.
Any ideas?
You're getting that error because you're trying to multiply a timestamp by 1440, rather than the difference between the date and timestamp - multiplication has higher precedence than subtraction. You also have HH245 instead of HH24 in your format model; trunc(sysdate, 'YYYY-MM-DD') will throw "ORA-01898: too many precision specifiers"; and if you changed that to to_char(sysdate, 'YYYY-MM-DD') || ... you would be subtracting a timestamp from a date which gives you an interval, not a number, and would throw a different ORA-00932 error.
Fixing all of those issues, you would end up with:
trunc((sysdate - to_date(to_char(sysdate, 'YYYY-MM-DD') || ' 12:30', 'YYYY-MM-DD HH24:MI')) * 1440, 1)
which would evaluate to 354.2 for me at the moment.
You don't need to convert to or from string though. You can get 12:30 today with:
trunc(sysdate) + (12.5/24)
where trunc(sysdate) gives you midnight today, and 12.5/24 gives you the fraction of a day for 12.5 hours. You can then subtract that from the current time:
sysdate - (trunc(sysdate) + (12.5/24))
to get the difference in days, then multiple that result by 1440 for the number of minutes, and truncate to a single decimal place.
Quick demo with current date and 12:30 today plus the difference three ways:
select sysdate,
trunc(sysdate) + (12.5/24) as "12:30",
sysdate - (trunc(sysdate) + (12.5/24)) as diff_days,
(sysdate - (trunc(sysdate) + (12.5/24))) * 1440 as diff_mins,
trunc((sysdate - (trunc(sysdate) + (12.5/24))) * 1440, 1) as result
from dual;
SYSDATE 12:30 DIFF_DAYS DIFF_MINS RESULT
------------------- ------------------- ---------- ---------- ----------
2019-08-14 18:24:16 2019-08-14 12:30:00 .246018519 354.266667 354.2
As #GordonLinoff hinted, the today-at-12:30 part could be achieved with an interval instead of a fractional day, e.g.:
trunc(sysdate) + 12.5 * interval '1' hour
or more simply and explicitly:
trunc(sysdate) + interval '12:30' hour to minute
You seem to want your final result as the number of minutes, but you could also get it as an interval, either by converting the current number-of-days intermediate result (or number of minutes) with numtodsinterval():
numtodsinterval(sysdate - (trunc(sysdate) + interval '12:30' hour to minute), 'DAY')
which is a bit convoluted, or directly:
select systimestamp,
systimestamp - trunc(sysdate) - interval '12:30' hour to minute as result
from dual;
SYSTIMESTAMP RESULT
------------------------------------ -------------------
2019-08-14 18:24:16.830116000 +01:00 +00 05:54:16.830116
db<>fiddle

How to run a query for every date for last 3 month

I have a table(pkg_date) in redshift. I want to fetch some data for every date for the last 3 months.
Here is my query
select * from pkg_data where scan_date < current_date;
How can I use current_date as a variable in the query itself and run this query for every date from April 1.
I have set a cron job which will run in every hour. In every hour it should run with different current_date
SELECT *
FROM pkg_data
WHERE scan_date > CURRENT_DATE - INTERVAL '3 months'
Be careful — Redshift works in UTC, so the CURRENT_DATE might suffer from timezone effects and be +/- what you expect sometimes.
SELECT
CURRENT_DATE,
(CURRENT_DATE - INTERVAL '3 months')::date
Returns:
2018-06-21 2018-03-21
Also be careful with strange lengths of months!
SELECT DATE '2018-05-31' - INTERVAL '3 months'
returns:
2018-02-28 00:00:00
Notice that it gave the last day of the month (31st vs 28th).
By the way, you can use DATE '2018-05-31' or '2018-05-31'::DATE, and also INTERVAL '3 months' or '3 months'::INTERVAL to convert types.
Use dateadd() for getting date 3 moth old day and GETDATE() for get current date.
ie code will look like.
select * from pkg_data where scan_date < dateadd(month,-3,GETDATE());
for cron refer How to execute scheduled SQL script on Amazon Redshift?

Last date with time of the month

Need your help to conclude the query to fetch last date time of the sysdate month.
select to_char(last_day(sysdate),'DD-Mon-YYYY HH24:MI:SS') from dual
it gives last date as expected, but I need time as 23:59:00 which is not possible thru above query.
You could use TRUNC on next day i.e. SYSDATE + 1, and then subtract 60 seconds i.e. 60/86400 to get the desired output.
SQL> SELECT to_char((trunc(last_day(sysdate)) +1) - 60/86400,'DD-Mon-YYYY HH24:MI:SS') dt
2 FROM dual;
DT
--------------------
29-Feb-2016 23:59:00
SQL>
You could also use interval '1' minute or interval '60' second instead of 60/86400.
If you just want it for display for some reason you can hard-code the time into the format mask:
select to_char(last_day(sysdate), 'DD-Mon-YYYY "23:59:00"') from dual;
But you probably really want it as a date object, in which case you can add 23 hours and 59 minutes to the truncated (midnight) date, wchi is 1439 of the 1440 minutes in a day:
select to_char(trunc(last_day(sysdate)) + 1439/1440, 'DD-Mon-YYYY HH24:MI:SS')
from dual;
Or you can go to the next day and remove a minute, either with fractional days or with intervals:
select to_char(trunc(last_day(sysdate)) + interval '1' day - interval '1' minute,
'DD-Mon-YYYY HH24:MI:SS') from dual;
Generally if you're working with time periods you want to include up to 23:59:59, which you can also do with any of those methods, but as Damien_The_Unbeliever said in a comment, it's easier to compare against the start of the next period (e.g. < add_months(trunc(sysdate, 'MM'), 1). It's easy to accidentally miss part of a day by not taking the time into account properly, particularly if you actually have a timestamp rather than a date.

SYSDATE but specify the time

I want to say the follow but substitute the date with SYSDATE but the time between is what I want to adjust. What would the syntax be?
where mydatefield between SYSDATE+'0001' and SYSDATE+'2359'
...
WHERE TO_CHAR( MOPACTIVITY.MOPNOTIFICATIONSENDAT , 'yyyy-mm-dd hh24:mi' )
BETWEEN '2013-07-26 00:00:01' AND '2013-07-26 23:59:59'
;
SYSDATE (or any other date column) in Oracle has the time component. So you need to strip that off and then add the hours/minutes/time condition.
Eg. to say current day 10:00 AM to 3:00 PM, you can say
date_column between (trunc(sysdate) + 10/24) and (trunc(sysdate) + 15/24)
Oracle date arithmetic works on the day level. so, +1 will give you the next day, 1/24 will give you an hour and 10/24 will give you 10:00 AM in the current day.
SQL> alter session set nls_date_format = 'DD-Mon-YYYY HH:MI:SS AM';
Session altered.
1 select sysdate,
2 trunc(sysdate),
3 trunc(sysdate) + 10/24,
4 trunc(sysdate) + 15/24
5* from dual
SQL> /
SYSDATE 26-Jul-2013 06:26:07 PM
TRUNC(SYSDATE) 26-Jul-2013 12:00:00 AM
TRUNC(SYSDATE)+10/24 26-Jul-2013 10:00:00 AM
TRUNC(SYSDATE)+15/24 26-Jul-2013 03:00:00 PM
For your question, you seem to be interested between current day and next day, so you can try adding + 1 to the date directly, once you strip the time component.
date_column >= trunc(sysdate) and
date_column < trunc(sysdate)+1
The best way to do this is to leave your MOPACTIVITY.MOPNOTIFICATIONSENDAT as a DATE type. That allows Oracle to optimize the query if there happens to be an index on the column. I'd recommend something like this:
WHERE MOPACTIVITY.MOPNOTIFICATIONSENDAT >= TRUNC(SYSDATE)
AND MOPACTIVITY.MOPNOTIFICATIONSENDAT < TRUNC(SYSDATE) + 1
That boils down to "greater than or equal to today at midnight" and "less than tomorrow at midnight".
We can also trunc both the dates and then compare the result
where TRUNC(MOPACTIVITY.MOPNOTIFICATIONSENDAT) = TRUNC(SYSDATE)
TRUNC Removes the timestamp from the dates