SQL , Analytical Functions , rownumber - sql

I need to get same rownumber or numeric value in SQL to group values that match conditions like the following example:
If we have same Agent name and the time variance between current row and preceding row value is less than 06:00 hours after applying partition by name and ordering by time
then add same rownumber else increase it.
example for row data and output of rownumber:
person date_time rownumber
A 01/04/2018 10:00 1
A 01/04/2018 13:00 1
A 01/04/2018 14:00 1
A 01/04/2018 15:00 1
A 01/04/2018 23:00 2
A 02/04/2018 03:00 2
A 02/04/2018 12:00 3
A 02/04/2018 16:00 3
B 01/04/2018 17:00 4
B 01/04/2018 20:30 4
C 01/04/2018 18:00 5
C 01/04/2018 22:00 5

You can do this with a combination of LAG and SUM analytic functions, like so:
WITH your_table AS (SELECT 'A' person, to_date('01/04/2018 10', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'A' person, to_date('01/04/2018 13', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'A' person, to_date('01/04/2018 14', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'A' person, to_date('01/04/2018 15', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'A' person, to_date('01/04/2018 23', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'A' person, to_date('02/04/2018 03', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'A' person, to_date('02/04/2018 12', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'A' person, to_date('02/04/2018 16', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'B' person, to_date('01/04/2018 17', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'B' person, to_date('01/04/2018 20', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'C' person, to_date('01/04/2018 18', 'dd/mm/yyyy hh24') date_time FROM dual UNION ALL
SELECT 'C' person, to_date('01/04/2018 22', 'dd/mm/yyyy hh24') date_time FROM dual)
SELECT person,
date_time,
SUM(period_change) OVER (ORDER BY person, date_time) rownumber
FROM (SELECT person,
date_time,
CASE WHEN date_time - LAG(date_time, 1, date_time - 7/24) OVER (PARTITION BY person ORDER BY date_time) > 6/24 THEN 1 ELSE 0 END period_change
FROM your_table);
PERSON DATE_TIME ROWNUMBER
------ ----------- ----------
A 01/04/2018 1
A 01/04/2018 1
A 01/04/2018 1
A 01/04/2018 1
A 01/04/2018 2
A 02/04/2018 2
A 02/04/2018 3
A 02/04/2018 3
B 01/04/2018 4
B 01/04/2018 4
C 01/04/2018 5
C 01/04/2018 5
This works by putting 1 in the additional column whenever a new group is triggered.
Once you have that, then you can do a running sum on that column. That means that after every group change, subsequent rows will be assigned the same number, up until the next group change.
N.B. As suggested by Peter Lang in the comments below, you might prefer to change the case statement generating the "period_change" column to:
CASE WHEN date_time - LAG(date_time) OVER (PARTITION BY person ORDER BY date_time) < 6/24 THEN 0 ELSE 1 END

Related

How to fill date range gaps Oracle SQL

With a given dataset:
WITH ranges AS (
select to_date('01.01.2021 00:00:00','DD.MM.YYYY hh24:mi:ss') date_from,
to_date('31.03.2021 00:00:00','DD.MM.YYYY hh24:mi:ss') date_to
from dual
union
select to_date('27.03.2021 00:00:00','DD.MM.YYYY hh24:mi:ss') date_from,
to_date('27.04.2021 00:00:00','DD.MM.YYYY hh24:mi:ss') date_to
from dual
union
select to_date('01.05.2021 00:00:00','DD.MM.YYYY hh24:mi:ss') date_from,
to_date('31.12.2021 00:00:00','DD.MM.YYYY hh24:mi:ss') date_to
from dual
)
SELECT * FROM ranges;
How to find the gap 28.04.2021-30.04.2021.? Also consider that there can be multiple gaps in between and ranges can overlap.
Any suggestion?
Try this query, tune to your needs:
WITH steps AS (
SELECT date_from as dt, 1 as step FROM ranges
UNION ALL
SELECT date_to as dt, -1 as step FROM ranges
)
SELECT dt as dt_from,
lead(dt) over (order by dt) as dt_to,
sum(step) over (order by dt) as cnt_ranges
FROM steps;
dt_from | dt_to | cnt_ranges
------------------------+-------------------------+-----------
2021-01-01 00:00:00.000 | 2021-03-27 00:00:00.000 | 1
2021-03-27 00:00:00.000 | 2021-03-31 00:00:00.000 | 2
2021-03-31 00:00:00.000 | 2021-04-27 00:00:00.000 | 1
2021-04-27 00:00:00.000 | 2021-05-01 00:00:00.000 | 0
2021-05-01 00:00:00.000 | 2021-12-31 00:00:00.000 | 1
2021-12-31 00:00:00.000 | | 0
You are modeling date ranges incorrectly; an interval ending at midnight on 02-14-2021, for example, should not include 02-14-2021. In your model it does.
This leads to unnecessary complications in all the queries you write against your model. In the solution below I need to add 1 to end dates first, do all the processing, and then subtract 1 at the end.
with
ranges (date_from, date_to) as (
select to_date('01.01.2021 00:00:00','DD.MM.YYYY hh24:mi:ss'),
to_date('31.03.2021 00:00:00','DD.MM.YYYY hh24:mi:ss')
from dual
union all
select to_date('27.03.2021 00:00:00','DD.MM.YYYY hh24:mi:ss'),
to_date('27.04.2021 00:00:00','DD.MM.YYYY hh24:mi:ss')
from dual
union all
select to_date('01.05.2021 00:00:00','DD.MM.YYYY hh24:mi:ss'),
to_date('31.12.2021 00:00:00','DD.MM.YYYY hh24:mi:ss')
from dual
)
select first_missing, last_missing - 1 as last_missing
from (
select dt as first_missing,
lead(df) over (order by dt) as last_missing
from (select date_from, date_to + 1 as date_to from ranges)
match_recognize(
order by date_from
measures first(date_from) as df, max(date_to) as dt
pattern (a* b)
define a as max(date_to) >= next (date_from)
)
)
where last_missing is not null
;
FIRST_MISSING LAST_MISSING
------------------- -------------------
28.04.2021 00:00:00 30.04.2021 00:00:00

Find peaks of data

So I have a table Integrations.
Inte
Start Date
End Date
Total_Duration
INT1
1/7/2021 7:16:00
1/7/2021 9:22:00
02:06:00
INt2
2/7/2021 3:48:00
2/7/2021 5:10:00
01:22:00
Output I need:
Running Time
No of Inte.
1/7/2021 7:00:00
1
1/7/2021 8:00:00
1
1/7/2021 9:00:00
1
2/7/2021 4:00:00
1
2/7/2021 5:00:00
1
Basically it want to plot the peak hour when most Integrations were running.
Sql query I wrote:
select time, sum(value) as No_of_Inte
from(
select round(Start_Date, 'HH24') as time, count(*) as value
from Integrations
group by Start_Date
)
group by time
order by time asc
But this does not consider Total Duration.
Output :
Running Time
No of Inte.
1/7/2021 7:00:00
1
2/7/2021 4:00:00
1
Also, new Integrations are added every day.
This can be done using a recursive query. First create the test data
CREATE TABLE integrations (inte,start_date, end_date)
AS
(
SELECT 'INT1', TO_DATE('1/7/2021 7:16:00','DD/MM/YYYY HH24:MI:SS'), TO_DATE('1/7/2021 9:22:00','DD/MM/YYYY HH24:MI:SS') FROM dual UNION ALL
SELECT 'INT2', TO_DATE('2/7/2021 3:48:00','DD/MM/YYYY HH24:MI:SS'), TO_DATE('2/7/2021 5:10:00','DD/MM/YYYY HH24:MI:SS') FROM dual
);
Now use a recursive query to loop through the hours between start and end date. Then group by hour to get the correct counts per hour.
WITH row_per_hours (id, run_hour, end_date) AS
(
SELECT inte,
TRUNC(start_date,'HH24'),
end_date
FROM integrations
UNION ALL
SELECT id,
run_hour + INTERVAL '1' HOUR,
end_date
FROM row_per_hours
WHERE run_hour + INTERVAL '1' HOUR < end_date
)
SELECT TO_CHAR(run_hour,'DD/MM/YYYY HH24:MI:SS') as running_time,
COUNT(id) as integration_count
FROM row_per_hours
GROUP BY TO_CHAR(run_hour,'DD/MM/YYYY HH24:MI:SS') ORDER BY 1;
RUNNING_TIME INTEGRATION_COUNT
------------------- -----------------
01/07/2021 07:00:00 1
01/07/2021 08:00:00 1
01/07/2021 09:00:00 1
02/07/2021 03:00:00 1
02/07/2021 04:00:00 1
02/07/2021 05:00:00 1
For 12C and above:
You may use lateral join to generate required number of rows per each interval. Since it looks like you need some rounding of dates towards neares hour, I've added round instead of trunc. Or is there any other reason for the first interval is treating 7:00 as inclusion?.
with a(Inte, start_dt, end_dt) as (
select
'INT1'
, to_date('1/7/2021 07:16:00', 'dd/mm/yyyy hh24:mi:ss')
, to_date('1/7/2021 09:22:00', 'dd/mm/yyyy hh24:mi:ss')
from dual union all
select
'INt2'
, to_date('2/7/2021 03:48:00', 'dd/mm/yyyy hh24:mi:ss')
, to_date('2/7/2021 05:10:00', 'dd/mm/yyyy hh24:mi:ss')
from dual
)
select /*+ gather_plan_statistics */
b.hour_
, count(1) as int_cnt
from a
outer apply (
select
round(a.start_dt + numtodsinterval(level - 1, 'HOUR'), 'hh24') as hour_
from dual
connect by round(start_dt, 'hh24') + numtodsinterval(level - 1, 'HOUR') <= trunc(end_dt, 'hh24')
) b
group by b.hour_
order by 1
HOUR_ | INT_CNT
:------------------ | ------:
2021-07-01 07:00:00 | 1
2021-07-01 08:00:00 | 1
2021-07-01 09:00:00 | 1
2021-07-02 04:00:00 | 1
2021-07-02 05:00:00 | 1
db<>fiddle here

Oracle SQL: Count a time period when the value was over the threshold

My table MEASUREMENTS (Oracle SQL 12) has 3 columns: DT - measurement timestamp, MEASUREMENT - value, THRESHOLD - upper limit.
Sometimes measurements are above the threshold. Trying to calculate time periods when the measurement value was higher than the threshold.
DT | MEASUREMENT | THRESHOLD
---------------+-------------+--------------------
04.08.16 01:10 | 60,5 | 70,0
04.08.16 01:20 | 65,5 | 70,0
04.08.16 01:30 | 68,1 | 70,0
04.08.16 01:40 | 70,1* | 70,0 //period start
04.08.16 01:50 | 70,1* | 70,0
04.08.16 02:00 | 70,75* | 70,0 //period end
04.08.16 02:10 | 53,5 | 70,0
04.08.16 02:20 | 50,15 | 70,0
04.08.16 02:30 | 52,15 | 70,0
04.08.16 02:40 | 53,15 | 70,0
Expected result (02:00-01:40=00:20):
DURATION | START | END
---------+----------------+---------------
00:20 | 04.08.16 01:40 | 04.08.16 02:00
You can use row_number() to identify the periods. This is a gaps-and-islands problem. The following returns each period where the measurement exceeds the threshold:
select max(dt) - min(dt) as duration, min(dt), max(dt)
from (select t.*,
row_number() over (order by dt) as seqnum,
row_number() over (partition by (case when measurement > threshold then 1 else 2 end), order by dt) as seqnum_t
from t
) t
where measurement > threshold
group by (seqnum - seqnum_t)
You can use the MATCH_RECOGNIZE clause (plus some extra info):
WITH t (DT, MEASUREMENT, THRESHOLD) AS (
SELECT TO_DATE('01:10', 'hh24:mi'), 60.5 , 70 FROM dual UNION ALL
SELECT TO_DATE('01:20', 'hh24:mi'), 65.5 , 70 FROM dual UNION ALL
SELECT TO_DATE('01:30', 'hh24:mi'), 68.1 , 70 FROM dual UNION ALL
SELECT TO_DATE('01:40', 'hh24:mi'), 70.1 , 70 FROM dual UNION ALL
SELECT TO_DATE('01:50', 'hh24:mi'), 70.1 , 70 FROM dual UNION ALL
SELECT TO_DATE('02:00', 'hh24:mi'), 70.75 , 70 FROM dual UNION ALL
SELECT TO_DATE('02:10', 'hh24:mi'), 53.5 , 70 FROM dual UNION ALL
SELECT TO_DATE('02:20', 'hh24:mi'), 50.15 , 70 FROM dual UNION ALL
SELECT TO_DATE('02:30', 'hh24:mi'), 52.15 , 70 FROM dual UNION ALL
SELECT TO_DATE('02:40', 'hh24:mi'), 53.15 , 70 FROM dual)
SELECT MEASUREMENT_MAX, match_num, FIRST_DT, LAST_DT, (LAST_DT-FIRST_DT)*24*60 AS DURATION
FROM t
MATCH_RECOGNIZE (
ORDER BY DT
MEASURES
FINAL MAX(MEASUREMENT) AS MEASUREMENT_MAX,
MATCH_NUMBER() AS match_num,
FINAL LAST(DT) AS LAST_DT,
FINAL FIRST(DT) AS FIRST_DT
PATTERN (a+)
DEFINE
a AS MEASUREMENT > THRESHOLD);
MEASUREMENT_MAX match_num FIRST_DT LAST_DT DURATION
70.75 3 01.06.2018 01:40:00 01.06.2018 02:00:00 20
You don't need to use two row_numbers, you can directly use it via cumulative approach :
select max(dt) - min(dt) as duration, min(dt), max(dt)
from (select *, row_number() over (order by dt) as seq,
sum(case when measurement > threshold then 1 else 0 end) over(order by dt) as grp
from table
) t
where measurement > threshold
group by (seq - grp);
Using row_number analytical function:
SQL> WITH measurements (DT, MEASUREMENT, THRESHOLD) AS (
2 select to_date('04.08.16 01:10', 'DD.MM.YY HH24:MI'), 60.5, 70.0 from dual union all
3 select to_date('04.08.16 01:20', 'DD.MM.YY HH24:MI'), 65.5, 70.0 from dual union all
4 select to_date('04.08.16 01:30', 'DD.MM.YY HH24:MI'), 68.1, 70.0 from dual union all
5 select to_date('04.08.16 01:40', 'DD.MM.YY HH24:MI'), 70.1, 70.0 from dual union all
6 select to_date('04.08.16 01:50', 'DD.MM.YY HH24:MI'), 70.1, 70.0 from dual union all
7 select to_date('04.08.16 02:00', 'DD.MM.YY HH24:MI'), 70.75, 70.0 from dual union all
8 select to_date('04.08.16 02:10', 'DD.MM.YY HH24:MI'), 53.5, 70.0 from dual union all
9 select to_date('04.08.16 02:20', 'DD.MM.YY HH24:MI'), 50.15, 70.0 from dual union all
10 select to_date('04.08.16 02:30', 'DD.MM.YY HH24:MI'), 52.15, 70.0 from dual union all
11 select to_date('04.08.16 02:40', 'DD.MM.YY HH24:MI'), 53.15, 70.0 from dual),
12 ---------------------
13 ---- end of data preparation
14 ---------------------
15 calculated_values AS (
16 SELECT DT,
17 MEASUREMENT,
18 THRESHOLD,
19 row_number() OVER (ORDER BY dt) - row_number() OVER (PARTITION BY CASE WHEN MEASUREMENT > THRESHOLD THEN 1 ELSE 0 END ORDER BY dt) rn,
20 CASE WHEN MEASUREMENT > THRESHOLD THEN 1 ELSE 0 END threshold_flag
21 FROM measurements)
22 SELECT cast(numtodsinterval(MAX(dt)-MIN(dt), 'DAY') AS INTERVAL DAY(0) TO SECOND(0)) AS duration,
23 MIN(dt) AS "START",
24 MAX(dt) AS "END"
25 FROM calculated_values
26 WHERE threshold_flag > 0
27 GROUP BY rn;
OUTPUT:
DURATION START END
----------- -------------------- --------------------
+0 00:20:00 8/4/2016 1:40:00 8/4/2016 2:00:00
Your query will be:
WITH calculated_values AS (
SELECT DT,
MEASUREMENT,
THRESHOLD,
row_number() OVER (ORDER BY dt) - row_number() OVER (PARTITION BY CASE WHEN MEASUREMENT > THRESHOLD THEN 1 ELSE 0 END ORDER BY dt) rn,
CASE WHEN MEASUREMENT > THRESHOLD THEN 1 ELSE 0 END threshold_flag
FROM measurements)
SELECT cast(numtodsinterval(MAX(dt)-MIN(dt), 'DAY') AS INTERVAL DAY(0) TO SECOND(0)) AS duration,
MIN(dt) AS "START",
MAX(dt) AS "END"
FROM calculated_values
WHERE threshold_flag > 0
GROUP BY rn;

Oracle Dynamic Range

So, i have this sample data:
Department | InitialDate | FinalDate
-------------------------------------------------------
1 | 01/01/2017 01:12:00 | 01/03/2017 00:00:08
1 | 01/03/2017 00:00:08 | 01/04/2017 05:00:01
1 | 01/04/2017 05:00:01 | 01/05/2017 02:00:00
2 | 01/05/2017 10:00:00 | 01/06/2017 11:00:08
2 | 01/06/2017 11:00:08 | 01/07/2017 04:04:00
3 | 01/07/2017 04:00:00 | 01/07/2017 15:00:22
1 | 01/07/2017 14:00:00 | 01/07/2017 18:00:08
1 | 01/07/2017 18:15:00 | 01/08/2017 22:00:00
3 | 01/12/2017 01:30:03 | 01/12/2017 18:00:00
1 | 01/13/2017 23:12:00 | 01/13/2017 23:59:08
and want to group it like this
Department | InitialDate | FinalDate
-------------------------------------------------------
1 | 01/01/2017 01:12:00 | 01/05/2017 02:00:00
2 | 01/05/2017 10:00:00 | 01/07/2017 04:04:00
3 | 01/07/2017 04:00:00 | 01/07/2017 15:00:22
1 | 01/07/2017 14:00:00 | 01/08/2017 22:00:00
3 | 01/12/2017 01:30:03 | 01/12/2017 18:00:00
1 | 01/13/2017 23:12:00 | 01/13/2017 23:59:08
I need to make groups by department and get the first and last date of each group, but the departments can repeat and for each time it occurs, I want the first and last date of that specific window. I already tried Analytic functions but nothing seems to work.
You can do it using the LAG analytic function to compare each row with the previous row:
SELECT department,
MIN( InitialDate ) AS InitialDate,
MIN( FinalDate ) AS FinalDate
FROM (
SELECT department,
InitialDate,
FinalDate,
SUM( grp_inc ) OVER ( ORDER BY FinalDate ) AS grp
FROM (
SELECT department,
InitialDate,
FinalDate,
CASE WHEN LAG( department ) OVER ( ORDER BY FinalDate ) = department
THEN 0
ELSE 1
END AS grp_inc
FROM table_name
)
)
GROUP BY department, grp
This is a type of "gaps-and-islands" problem. One method of solving it is by determining where groups of overlapping times start. Then use a cumulative sum to define each group:
select departmentid, min(initialdate), max(finaldate)
from (select t.*, sum(grp_starts) over (partition by departmentid order by initialdate) as grp
from (select t.*,
(case when exists (select 1
from t t2
where t2.departmentid = t.departmentid and
t.initialdate > t2.initialdate and
t.initialdate <= t2.finaldate
)
then 0 else 1
end) as grp_starts
from t
) t
) t
group by departmentid, grp;
Since you are looking for where the department changes and not where the department changes or the initialdate is not the same as the previous row's finaldate, you can use tabibitosan
WITH sample_data AS (SELECT 1 department, to_date('01/01/2017 01:12:00', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/03/2017 00:00:08', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 1 department, to_date('01/03/2017 00:00:08', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/04/2017 05:00:01', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 1 department, to_date('01/04/2017 05:00:01', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/05/2017 02:00:00', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 2 department, to_date('01/05/2017 10:00:00', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/06/2017 11:00:08', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 2 department, to_date('01/06/2017 11:00:08', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/07/2017 04:04:00', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 3 department, to_date('01/07/2017 04:00:00', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/07/2017 15:00:22', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 1 department, to_date('01/07/2017 14:00:00', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/07/2017 18:00:08', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 1 department, to_date('01/07/2017 18:15:00', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/08/2017 22:00:00', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 3 department, to_date('01/12/2017 01:30:03', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/12/2017 18:00:00', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual union all
SELECT 1 department, to_date('01/13/2017 23:12:00', 'mm/dd/yyyy hh24:mi:ss') initialdate, to_date('01/13/2017 23:59:08', 'mm/dd/yyyy hh24:mi:ss') finaldate from dual)
SELECT department,
MIN(initialdate) initialdate,
MAX(finaldate) finaldate
FROM (SELECT department,
initialdate,
finaldate,
row_number() OVER (ORDER BY initialdate)
- row_number() OVER (PARTITION BY department ORDER BY initialdate) grp
FROM sample_data sd)
GROUP BY department, grp
ORDER BY initialdate;
DEPARTMENT INITIALDATE FINALDATE
---------- ------------------- -------------------
1 01/01/2017 01:12:00 01/05/2017 02:00:00
2 01/05/2017 10:00:00 01/07/2017 04:04:00
3 01/07/2017 04:00:00 01/07/2017 15:00:22
1 01/07/2017 14:00:00 01/08/2017 22:00:00
3 01/12/2017 01:30:03 01/12/2017 18:00:00
1 01/13/2017 23:12:00 01/13/2017 23:59:08
This works by walking through and numbering all rows ordered by initial date and comparing them to walking through and numbering the rows for each department. When the department changes, the difference between the numbers changes. Where a department has consecutive rows in the initial dataset, the difference will remain the same for those rows. E.g. in your data, department 1 has 6 rows, the first 3 rows are the same as the first 3 rows of the initial data set, so the difference for those three rows is 0. The fourth and fifth department 1 rows are the 7th and 8th rows in the dataset, so the difference is 3 for those rows, etc.
This gives us a number that we can use, in conjunction with the department number, to group the data by. It's then a simple matter of finding the min/max dates within that group.

SQL grouping on time interval

I have a data set which is based on a timestamp. The Data set present record on every shut down occurrence in a 5 minute time interval. If a shut down occurred in the specific 5 min, then the record is added else no record. Thus no record means system has recovered
Date
07-Jul-15 12:05:00
07-Jul-15 12:10:00
07-Jul-15 12:15:00
07-Jul-15 12:35:00
07-Jul-15 12:40:00
07-Jul-15 12:45:00
07-Jul-15 12:50:00
07-Jul-15 13:05:00
07-Jul-15 13:10:00
07-Jul-15 13:15:00
I would like to query and return
1.Number of shutdowns: The Number of shut down in this case is 3 based on between
12:15 to 12:35
12:50 to 13:05
The system recovered
Period Between every shut down
Example:
1.From: 07-Jul-15 12:05:00 To: 07-Jul-15 12:15:00 Duration : 15 Mins
2.From: 07-Jul-15 12:35:00 To: 07-Jul-15 12:50:00 Duration : 20 Mins
There is a similar Question although a very different solution is required for this one.
would appreciate a fiddle example
WITH changes AS (
SELECT "DATE",
CASE WHEN LAG( "DATE" ) OVER ( ORDER BY "DATE" ) + INTERVAL '5' MINUTE = "DATE" THEN 0 ELSE 1 END AS has_changed_group
FROM TEST
), grps AS (
SELECT "DATE",
SUM( has_changed_group ) OVER ( ORDER BY "DATE" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS grp
FROM changes
)
SELECT MIN( "DATE" ) AS shutdown_start,
MAX( "DATE" ) AS shutdown_end,
MAX( "DATE" ) - MIN( "DATE" ) + INTERVAL '5' MINUTE AS shutdown_duration
FROM grps
GROUP BY grp;
Output:
SHUTDOWN_START SHUTDOWN_END SHUTDOWN_DURATION
---------------------------- ---------------------------- -----------------
07-JUL-15 12.05.00.000000000 07-JUL-15 12.15.00.000000000 0 0:15:0.0
07-JUL-15 12.35.00.000000000 07-JUL-15 12.50.00.000000000 0 0:20:0.0
07-JUL-15 13.05.00.000000000 07-JUL-15 13.15.00.000000000 0 0:15:0.0
Edit - Multiple machines:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TEST ( MACHINE_ID, "DATE" ) AS
SELECT 1, TIMESTAMP '2015-07-07 12:05:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 12:10:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 12:15:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 12:35:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 12:40:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 12:45:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 12:50:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 13:05:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 13:10:00' FROM DUAL
UNION ALL SELECT 1, TIMESTAMP '2015-07-07 13:15:00' FROM DUAL
UNION ALL SELECT 2, TIMESTAMP '2015-07-07 12:35:00' FROM DUAL
UNION ALL SELECT 2, TIMESTAMP '2015-07-07 12:40:00' FROM DUAL
UNION ALL SELECT 2, TIMESTAMP '2015-07-07 12:45:00' FROM DUAL
UNION ALL SELECT 2, TIMESTAMP '2015-07-07 13:00:00' FROM DUAL
UNION ALL SELECT 2, TIMESTAMP '2015-07-07 13:05:00' FROM DUAL
UNION ALL SELECT 2, TIMESTAMP '2015-07-07 13:10:00' FROM DUAL
UNION ALL SELECT 2, TIMESTAMP '2015-07-07 13:15:00' FROM DUAL;
Query 1:
WITH changes AS (
SELECT MACHINE_ID,
"DATE",
CASE WHEN LAG( "DATE" ) OVER ( PARTITION BY MACHINE_ID ORDER BY "DATE" ) + INTERVAL '5' MINUTE = "DATE" THEN 0 ELSE 1 END AS has_changed_group
FROM TEST
), grps AS (
SELECT MACHINE_ID,
"DATE",
SUM( has_changed_group ) OVER ( PARTITION BY MACHINE_ID ORDER BY "DATE" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS grp
FROM changes
)
SELECT MACHINE_ID,
TO_CHAR( MIN( "DATE" ), 'YYYY-MM-DD HH24:MI:SS' ) AS shutdown_start,
TO_CHAR( MAX( "DATE" ), 'YYYY-MM-DD HH24:MI:SS' ) AS shutdown_end,
TO_CHAR( MAX( "DATE" ) - MIN( "DATE" ) + INTERVAL '5' MINUTE ) AS shutdown_duration
FROM grps
GROUP BY MACHINE_ID, grp
ORDER BY 1,2
Results:
| MACHINE_ID | SHUTDOWN_START | SHUTDOWN_END | SHUTDOWN_DURATION |
|------------|---------------------|---------------------|-------------------------------|
| 1 | 2015-07-07 12:05:00 | 2015-07-07 12:15:00 | +000000000 00:15:00.000000000 |
| 1 | 2015-07-07 12:35:00 | 2015-07-07 12:50:00 | +000000000 00:20:00.000000000 |
| 1 | 2015-07-07 13:05:00 | 2015-07-07 13:15:00 | +000000000 00:15:00.000000000 |
| 2 | 2015-07-07 12:35:00 | 2015-07-07 12:45:00 | +000000000 00:15:00.000000000 |
| 2 | 2015-07-07 13:00:00 | 2015-07-07 13:15:00 | +000000000 00:20:00.000000000 |
Solution using Tom Kyte's "carry down" technique:
with test1 as (
select mydate,
-- mark starting records in each group
case when NVL((mydate - lag(mydate) over (order by mydate))*24*60,10) > 5
then row_number() over (order by mydate) end as group_id
from test),
test2 as (
select mydate,
-- propagate group_id to all records
LAST_VALUE(group_id IGNORE NULLS) over (order by mydate) as group_id
from test1)
select min(mydate) shutdown_from, max(mydate) shutdown_to
from test2
group by group_id;
Output
SHUTDOWN_FROM SHUTDOWN_TO
------------------- -------------------
07.07.0015 12:05:00 07.07.0015 12:15:00
07.07.0015 12:35:00 07.07.0015 12:50:00
07.07.0015 13:05:00 07.07.0015 13:15:00