Reputation: 601
In an Oracle table I have data coming in on 15 minute intervals across many different unique IDs. For example, the data might look something like this:
ObjectID Date
123 1/1/2016 00:00:00
123 1/1/2016 00:15:00
123 1/1/2016 00:30:00
123 1/1/2016 00:45:00
123 1/1/2016 01:00:00
456 1/1/2016 00:00:00
456 1/1/2016 00:15:00
456 1/1/2016 00:30:00
456 1/1/2016 00:45:00
456 1/1/2016 01:00:00
789 1/1/2016 00:00:00
789 1/1/2016 00:15:00
789 1/1/2016 00:30:00
789 1/1/2016 00:45:00
789 1/1/2016 01:00:00
This is a very simple example. In reality, the objectIDs are not sorted in a uniform order and there's a possibility of missing intervals. Thus the data really looks more like this:
ObjectID Date
456 1/1/2016 00:15:00
456 1/1/2016 00:30:00
123 1/1/2016 00:30:00
123 1/1/2016 00:45:00
123 1/1/2016 01:00:00
456 1/1/2016 00:45:00
456 1/1/2016 01:00:00
789 1/1/2016 00:45:00
789 1/1/2016 01:00:00
789 1/1/2016 00:00:00
789 1/1/2016 00:15:00
789 1/1/2016 00:30:00
I would like to be able to get a count of the missing intervals across all of the objectIDs for a specific date range. In the above, there are three missing intervals:
123 1/1/2016 00:00:00
123 1/1/2016 00:15:00
456 1/1/2016 00:00:00
I'm looking for a query that would catch these and return a count of 3 if given a date range of 1/1/2016 00:00:00 - 1/1/2016 1:00:00.
Upvotes: 1
Views: 64
Reputation: 738
Let me take a shot at it.... (Aleksej i took off from your answer).
with
--
-- Test case supplied
--
test(ObjectID, Date_) as
(
select 456, to_date('1/1/2016 00:15:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 456, to_date('1/1/2016 00:30:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 123, to_date('1/1/2016 00:30:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 123, to_date('1/1/2016 00:45:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 123, to_date('1/1/2016 01:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 456, to_date('1/1/2016 00:45:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 456, to_date('1/1/2016 01:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 00:45:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 01:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 00:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 00:15:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 00:30:00', 'dd/mm/yyyy hh24:mi:ss') from dual
),
--
-- Intervals generates all possible 15 minutes intervals within the given parameters
--
intervals (Date_) as
(
select to_date('1/1/2016 00:00:00', 'dd/mm/yyyy hh24:mi:ss') + ((15/1440) * (level - 1))
from dual
connect by level <= 24 * (to_date('1/1/2016 01:00:00', 'dd/mm/yyyy hh24:mi:ss') - to_date('1/1/2016 00:00:00', 'dd/mm/yyyy hh24:mi:ss')) * 4
)
--
-- Simple minus to find the missing intervals
--
select distinct test.ObjectID, to_char(intervals.Date_ ,'YYYY/MM/DD HH24:MI:SS') dt
from test,
intervals
minus
select test.ObjectID, to_char(test.Date_,'YYYY/MM/DD HH24:MI:SS') dt
from test
/
When I execute the missing intervals:
OBJECTID DT
---------- -------------------
123 2016/01/01 00:00:00
123 2016/01/01 00:15:00
456 2016/01/01 00:00:00
Upvotes: 1
Reputation: 22959
If you only need counting, an approach could be count the expected intervals and make the difference with the number of intervals really found:
with test(ObjectID, Date_) as
(
select 456, to_date('1/1/2016 00:15:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 456, to_date('1/1/2016 00:30:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 123, to_date('1/1/2016 00:30:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 123, to_date('1/1/2016 00:45:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 123, to_date('1/1/2016 01:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 456, to_date('1/1/2016 00:45:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 456, to_date('1/1/2016 01:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 00:45:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 01:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 00:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 00:15:00', 'dd/mm/yyyy hh24:mi:ss') from dual union all
select 789, to_date('1/1/2016 00:30:00', 'dd/mm/yyyy hh24:mi:ss') from dual
),
input_dates(start_date, end_date) as
( select to_date('1/1/2016 00:00:00', 'dd/mm/yyyy hh24:mi:ss'),
to_date('1/1/2016 01:00:00', 'dd/mm/yyyy hh24:mi:ss') from dual)
--
select (count( distinct objectId) * (input_dates.end_date - input_dates.start_date) * 24 * 5)
-
sum(
case when date_ between input_dates.start_date and input_dates.end_date
then 1
else 0
end
) as missing
from test, input_dates
In case of an objectID
that is in your table, but does not have any interval in the input period, this will return 5, to say that it's missing all the intervals.
If you want to check only missing intervals on IDs having at least one interval in the period, you can simply remove the BETWEEN
condition from the SUM and add in as WHERE
clause
Upvotes: 1
Reputation: 12169
I think something like this may work. May be more efficient method.
select count(*) from your_table
where your_table.date not in
(
SELECT dateval
FROM
( WITH dates AS
(SELECT to_date('01/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') dstart,
to_date('01/01/2016 01:00:00','MM/DD/YYYY HH24:MI:SS') dend
FROM dual
)
SELECT dstart + rownum/96.0 dateval
FROM dates
CONNECT BY rownum <=
(SELECT (dend - dstart)*96 FROM dates
)
)
)
The "connect by" counts the number of 15 minute intervals between the start/end date, then generates each of the date/times in 15 minute increments starting with the start date/time.
Upvotes: 1