LAG Function in Oracle - sql

I have a table (incident) that has column Create_date(DataType=Date).
I want to get difference in Days OR Hours from Previous Record. Like the screenshot below.
From Second Record Create_Date I want to minus First Create_Date and from Third Create Date to Second and so on. I'm using LAG function in Oracle, but not sure how its calculating there. Could any one please help me regarding that issue.
incident.create_date - lag(incident.create_date,1) OVER (ORDER BY incident.create_date) AS CREATEDATE_DIFF,
RN 1 We have Create_date (05/01/017 10:40:17 AM

Date differences in Oracle are calculated in numbers of days. If the difference is less than a day, you're going to get a value of less than 1 returned.
If you want to convert that into hours, you'll have to multiply the result by 24, for minutes multiply by 24*60 and for seconds it's 24*60*60.
e.g.:
select sysdate - trunc(sysdate) diff_in_days,
(sysdate - trunc(sysdate))*24 diff_in_hours,
(sysdate - trunc(sysdate))*24*60 diff_in_mins,
(sysdate - trunc(sysdate))*24*60*60 diff_in_secs
from dual;
DIFF_IN_DAYS DIFF_IN_HOURS DIFF_IN_MINS DIFF_IN_SECS
------------ ------------- ------------ ------------
0.4342245370 10.4213888888 625.28333333 37517
You may then wish to apply ROUND (or maybe TRUNC/CEIL) depending on how you want the output to look like (e.g. to 2 d.p., to nearest minute, etc).

If you subtract one date from another you will get the difference in days (or fractions thereof) as a number.
You can get the days/hours/minutes/seconds of this using an interval:
SELECT EXTRACT( DAY FROM createdate_diff ) AS days,
EXTRACT( HOUR FROM createdate_diff ) AS hours,
EXTRACT( MINUTE FROM createdate_diff ) AS minutes,
EXTRACT( SECOND FROM createdate_diff ) AS seconds,
createdate_diff
FROM (
SELECT NUMTODSINTERVAL(
create_date - lag(create_date) OVER (ORDER BY create_date),
'DAY'
) AS CREATEDATE_DIFF
FROM incident
);
Or you can perform the same calculations manually:
SELECT TRUNC( createdate_diff ) AS days,
TRUNC( MOD( createdate_diff * 24, 24 ) ) AS hours,
TRUNC( MOD( createdate_diff * 24 * 60, 60 ) ) AS minutes,
MOD( createdate_diff * 24 * 60 * 60, 60 ) AS seconds,
createdate_diff
FROM (
SELECT create_date - lag(create_date) OVER (ORDER BY create_date)
AS CREATEDATE_DIFF
FROM incident
);

Use
......
(incident.create_date -
lag(incident.create_date,1) OVER (ORDER BY incident.create_date))
*24*60
AS CREATEDATE_DIFF_IN_MINS,.....
to get the output in minutes, which seams suitable for your sample data. Or multiply further by 60 to get output in seconds.

Related

Select Data From Multiple Days Between Certain Times (Spanning 2 days)

I need to know how many entries appear in my DB for the past 7 days with a timestamp between 23:00 & 01:00...
The Issue I have is the timestamp goes across 2 days and unsure if this is even possible in the one query.
So far I have come up with the below:
select trunc(timestamp) as DTE, extract(hour from timestamp) as HR, count(COLUMN) as Total
from TABLE
where trunc(timestamp) >= '12-NOV-19' and
extract(hour from timestamp) in ('23','00','01')
group by trunc(timestamp), extract(hour from timestamp)
order by 1,2 desc;
The result I am hoping for is something like this:
DTE | Total
20-NOV-19 5
19-NOV-19 4
18-NOV-19 4
17-NOV-19 6
Many thanks
Filter on the day first comparing it to TRUNC( SYSDATE ) - INTERVAL '7' DAY and then consider the hours by comparing the timestamp to itself truncated back to midnight with an offset of a number of hours.
select trunc(timestamp) as DTE,
extract(hour from timestamp) as HR,
count(COLUMN) as Total
from TABLE
WHERE timestamp >= TRUNC( SYSDATE ) - INTERVAL '7' DAY
AND ( timestamp <= TRUNC( timestamp ) + INTERVAL '01:00' HOUR TO MINUTE
OR timestamp >= TRUNC( timestamp ) + INTERVAL '23:00' HOUR TO MINUTE
)
group by trunc(timestamp), extract(hour from timestamp)
order by DTE, HR desc;
Subtract or add an hour to derive the date. I'm not sure what date you want to assign to each period, but the idea is:
select trunc(timestamp - interval '1' hour) as DTE,
count(*) as Total
from t
where trunc(timestamp - interval '1' hour) >= DATE '2019-11-12' and
extract(hour from timestamp) in (23, 0)
group by trunc(timestamp - interval '1' hour)
order by 1 desc;
Note: If you want times between 11:00 p.m. and 1:00 a.m., then you want the hour to be 23 or 0.

Oracle epoch date function for the previous month

I have my table with EPOCH date system, i want to select data from previous month only. I have googled a lot but the only results i find is related to datetime system. Can you please help?
I will use this SQL Query to import data to Power BI.
You can convert your date range to an epoch using ( date_to_convert - DATE '1970-01-01' ) * 24 * 60 * 60 (assuming your epoch is in seconds since 1970).
You want to find values that are greater-than-or-equal-to the start of the previous month and before the start of the current month:
SELECT *
FROM your_table
WHERE epoch_column >= ( ADD_MONTHS( TRUNC(SYSDATE,'MM'), - 1 ) - DATE '1970-01-01' ) * 24 * 60 * 60
AND epoch_column < ( TRUNC( SYSDATE, 'MM' ) - DATE '1970-01-01' ) * 24 * 60 * 60
If you use midnight of the last day of the previous month as your upper bound then you will miss all values that are on that last day but are after midnight.
You can convert a unix epoch (number of seconds since January 1st, 1970) to a date as follows :
TO_DATE('01/01/1970', 'dd/mm/yyyy') + epoch_column / 60 / 60 / 24
The division turns the epoch into a number of days, that can be added to the original date.
If you want to filter this on the previous month, then :
TO_DATE('01/01/1970', 'dd/mm/yyyy') + epoch_column / 60 / 60 / 24
BETWEEN ADD_MONTHS(TRUNC(sysdate, 'mm'), -1)
AND LAST_DAY(ADD_MONTHS(TRUNC(sysdate, 'mm'), -1))
Or better yet :
epoch_column
BETWEEN
(
ADD_MONTHS(TRUNC(sysdate, 'mm'), -1)
- TO_DATE('01/01/1970', 'dd/mm/yyyy')
) * 60 * 60 * 24
AND (
LAST_DAY(ADD_MONTHS(TRUNC(sysdate, 'mm'), -1))
- TO_DATE('01/01/1970', 'dd/mm/yyyy')
) * 60 * 60 * 24
This solution should be more efficient, as no operation is performed on the column being filtered, hence it should be able to take advantage of an existing index on epoch_column.

Oracle : SQL : Return seconds between two datetime fields

All, I have read a number of solutions and from what I saw doesn't resolve my issue. The reason, I have one datetime stamp right before midnight AND I have one datetime stamp after midnight.
Using the EXTRACT function doesn't cut it because the hours/min/sec before midnight will be 86k seconds and the hours/min/sec after will be within the 100s. The number of seconds should be around 240, but in this scenario it is -86,000+
Here is what I was using, but again, doesn't work for those datetime stamps before and after midnight.
( ( EXTRACT( hour FROM CALL_START_TIME ) * 60 * 60 ) +
( EXTRACT( minute FROM CALL_START_TIME ) * 60 ) +
( EXTRACT( second FROM CALL_START_TIME ) ) ) -
( ( EXTRACT( hour FROM CALL_END_TIME ) * 60 * 60 ) +
( EXTRACT( minute FROM CALL_END_TIME ) * 60 ) +
( EXTRACT( second FROM CALL_END_TIME ) ) ) AS SPEED_TO_ANSWER
If you subtract start time from end time, you get the number of days, which may be something like 0.00123 in your case. Multiply by 24 to get to hours, then with 60 to get minutes, again with 60 to get seconds:
(CALL_END_TIME - CALL_START_TIME) * 24 * 60 * 60

SQL statement dynamically using current time to choose a time frame in a field (Oracle)

All, I have something that is stumping me and I have seen a lot of examples, but nothing is helping solve this.
I have time frames like 03:30:00 to 11:29:59 that I work with (say shift times). I want to dynamically query data for the last shift based on the current shift.
Example: if it is currently between 11:30:00 AM and 7:29:59 PM, I want get the last shift that was between 03:30:00 AM and 11:30:00 AM.
This would look like an if statement in my mind:
If time between .... then
select time between....
elseif time between.... then
select time between...
I tried many combinations and can't figure this out. I think I would need a CASE and maybe a subquery? or maybe DECODE will work?
SELECT CAST(ccd.DATEc AS TIME) as time_occured,
FROM db.datatb ccd
WHERE ccd.DATE > SYSDATE - interval '1440' minute
AND (
((TO_CHAR(SYSDATE, 'hh24:mi:ss')BETWEEN '03:30:00' AND '11:29:59' IN (SELECT
ccd.DATEc FROM db.datatb WHERE (CAST(ccd.DATEc AS TIME)NOT BETWEEN '03:30:00
AM' AND '07:29:59 PM')))
OR (TO_CHAR(SYSDATE, 'hh24:mi:ss')BETWEEN '11:30:00' AND '19:29:59' IN
(SELECT ccd.DATEc FROM db.datatb WHERE (CAST(ccd.DATEc AS TIME) BETWEEN
'03:30:00 AM' AND '11:29:59 AM')))
OR (TO_CHAR(SYSDATE, 'hh24:mi:ss')NOT BETWEEN '03:30:00' AND '19:29:59' IN
(SELECT ccd.DATEc FROM db.datatb WHERE (CAST(ccd.DATEc AS TIME) BETWEEN
'11:30:00 AM' AND '07:29:59 PM')))
)
SELECT *
FROM db.datatb
CROSS JOIN
( SELECT TRUNC( SYSDATE - INTERVAL '210' MINUTE )
+ NUMTODSINTERVAL(
TRUNC(
( SYSDATE - INTERVAL '210' MINUTE
- TRUNC( SYSDATE - INTERVAL '210' MINUTE )
) * 3
) * 480
+ 210,
'MINUTE'
) AS current_shift_start
FROM DUAL
) css
WHERE DATEc >= css.current_shift_start - INTERVAL '8' HOUR
AND DATEc < css.current_shift_start;
Explanation:
The shifts are 8 hours each starting at 03:30 (or 210 minutes past midnight); so SYSDATE - INTERVAL '210' MINUTE will move offset the times so that after this offset they start at 00:00, 08:00 and 16:00 which is thirds of a day.
date_value - TRUNC( date_value ) calculates the fraction of a day (between 0 and 1) that the time component represents; so TRUNC( ( date_value - TRUNC( date_value ) ) * 3 ) maps that fraction of the day to 0, 1 or 2 corresponding to whether it is in the 1st, 2nd or 3rd 8 hour period of the day. Multiple that value by 480 minutes and then add the 210 minutes that the date was originally offset by and you have the minutes past the start of the day that the shift starts.

How to filter query with EXTRACT statement oracle

I have this query where I get the difference between the SYSDATE and some date column. I need to add another filter to this query to filter the records where DAY = 0. is it possible?
SELECT REQUEST_ID,MIG_STATUS,
EXTRACT(Day FROM( sysdate - START_DATE ) DAY TO SECOND) as Day,
EXTRACT(HOUR FROM( sysdate - START_DATE) DAY TO SECOND) as Hour,
EXTRACT(Minute FROM(sysdate - START_DATE) DAY TO SECOND) as Minute,
EXTRACT(SECOND FROM(sysdate - START_DATE) DAY TO SECOND) as Second
FROM NET_MIG
results:
T1_ID DAY HOUR MINUTE SECOND
1 2,817 12 12 8
2 2,817 8 26 32
3 0 1 0 0
3 1 8 26 32
3 0 13 0 0
3 0 0 59 0
3 0 0 59 0
need to add filter
where Day = 0
is this the correct approach?
just to be more clear, as a result I need to get the records where the
difference between the dates is less than 1 day.
You can use:
SELECT REQUEST_ID,MIG_STATUS
-- rest of columns
FROM NET_MIG
WHERE START_DATE >= (SYSDATE - 1);
If you want records from last 5 minutes just use:
SELECT REQUEST_ID,MIG_STATUS
-- rest of columns
FROM NET_MIG
WHERE START_DATE >= (SYSDATE - 5 * 1/(24 * 60));
The same for 1 hour:
WHERE START_DATE >= (SYSDATE - 1/24);
EDIT:
As #a-horse-with-no-name in comment you can use INTERVAL:
WHERE START_DATE >= (SYSDATE - INTERVAL '5' MINUTE)
for better readability.
And #kordirko comment:
This solution is SARG-able. It means it will use index on START_DATE if exists any, where EXTRACT(Day FROM( sysdate - START_DATE ) DAY TO SECOND) = 0 will skip index on that column and enforce full table scan.
SELECT * FROM
(SELECT REQUEST_ID,MIG_STATUS,
EXTRACT(Day FROM( sysdate - START_DATE ) DAY TO SECOND) as Day,
EXTRACT(HOUR FROM( sysdate - START_DATE) DAY TO SECOND) as Hour,
EXTRACT(Minute FROM(sysdate - START_DATE) DAY TO SECOND) as Minute,
EXTRACT(SECOND FROM(sysdate - START_DATE) DAY TO SECOND) as Second
FROM NET_MIG)
WHERE Day=0
I'm not sure this is the best approach, since you should be using direct date comparisons as much as possible, but this is one way you can re-use your custom fields in a where clause.