How to spread the average between two intervals in oracle

*爱你&永不变心* 提交于 2019-12-03 21:48:06

EDIT: Added union to include the final missing row

Some thing like this may work. Assuming the input data is in table a,

with b as
(select level-1 lev
from dual
connect by level <= 60
),
v as
(
select start_date, value current_value, lead(value) over (order by start_date) next_value
from a
)
select start_date+ (lev)/(24*60), (current_value*((60-(b.lev))/60) + next_value*(b.lev)/60) avg_value
from v, b
where v.next_value is not null
union
select start_date, current_value
from v
where v.next_value is null
order by 1

You can use recursive subquery factoring to do the interval halving, and find the weighted average (or whatever this calculation is supposed to be finding) for each step:

with r (period_start, period_average, step, step_start, step_end, step_average) as (
  select period_start,
    period_average,
    1,
    period_start + ((lead(period_start) over (order by period_start) - period_start)/2),
    lead(period_start) over (order by period_start) - 1/86400,
    (period_average + lead(period_average) over (order by period_start))/2
  from averages
  union all
  select period_start,
    period_average,
    r.step + 1,
    case when r.step_start = period_start + 60/86400 then period_start
      else trunc(period_start + ((r.step_start - period_start)/2) + 30/86400, 'MI')
      end,
    r.step_start - 1/86400,
    case when r.step_start = period_start + 60/86400 then period_average
      else (period_average + r.step_average)/2
      end
  from r
  where r.step_start > r.period_start
)
--cycle step_start set is_cycle to 1 default 0
select * from r
where step_start is not null
order by step_start;

The anchor member gets the initial half-hour slot and the next period's average value, via lead(), and uses those to calculate the initial (20+50)/2 etc.:

PERIOD_START     PERIOD_AVERAGE STEP STEP_START       STEP_END         STEP_AVERAGE
---------------- -------------- ---- ---------------- ---------------- ------------
2015-01-01 06:00             20    1 2015-01-01 06:30 2015-01-01 06:59     35.00000
2015-01-01 07:00             50    1 2015-01-01 07:30 2015-01-01 07:59     45.00000
2015-01-01 08:00             40    1 2015-01-01 08:30 2015-01-01 08:59     35.00000
...

The recursive member then repeats that process but with the previous step's period length and calculated average. I've now made it stop when it reaches the last minute in the period.

So that gives you the intermediate result set:

PERIOD_START     PERIOD_AVERAGE STEP STEP_START       STEP_END         STEP_AVERAGE
---------------- -------------- ---- ---------------- ---------------- ------------
2015-01-01 06:00             20    7 2015-01-01 06:00 2015-01-01 06:00     20.00000
2015-01-01 06:00             20    6 2015-01-01 06:01 2015-01-01 06:01     20.46875
2015-01-01 06:00             20    5 2015-01-01 06:02 2015-01-01 06:03     20.93750
2015-01-01 06:00             20    4 2015-01-01 06:04 2015-01-01 06:07     21.87500
2015-01-01 06:00             20    3 2015-01-01 06:08 2015-01-01 06:14     23.75000
2015-01-01 06:00             20    2 2015-01-01 06:15 2015-01-01 06:29     27.50000
2015-01-01 06:00             20    1 2015-01-01 06:30 2015-01-01 06:59     35.00000
2015-01-01 07:00             50    7 2015-01-01 07:00 2015-01-01 07:00     50.00000
2015-01-01 07:00             50    6 2015-01-01 07:01 2015-01-01 07:01     49.84375
2015-01-01 07:00             50    5 2015-01-01 07:02 2015-01-01 07:03     49.68750
2015-01-01 07:00             50    4 2015-01-01 07:04 2015-01-01 07:07     49.37500
2015-01-01 07:00             50    3 2015-01-01 07:08 2015-01-01 07:14     48.75000
2015-01-01 07:00             50    2 2015-01-01 07:15 2015-01-01 07:29     47.50000
2015-01-01 07:00             50    1 2015-01-01 07:30 2015-01-01 07:59     45.00000
2015-01-01 08:00             40    7 2015-01-01 08:00 2015-01-01 08:00     40.00000
2015-01-01 08:00             40    6 2015-01-01 08:01 2015-01-01 08:01     39.84375
2015-01-01 08:00             40    5 2015-01-01 08:02 2015-01-01 08:03     39.68750
2015-01-01 08:00             40    4 2015-01-01 08:04 2015-01-01 08:07     39.37500
2015-01-01 08:00             40    3 2015-01-01 08:08 2015-01-01 08:14     38.75000
2015-01-01 08:00             40    2 2015-01-01 08:15 2015-01-01 08:29     37.50000
2015-01-01 08:00             40    1 2015-01-01 08:30 2015-01-01 08:59     35.00000

You can then use another recursive CTE, or I think more simply a connect by clause, to expand each of those steps into the appropriate number of minutes, each with the same 'average' value:

with r (period_start, period_average, step, step_start, step_end, step_average)
as (
  ...
)
select step_start + (level - 1)/24/60 as min_start, step_average
from r
where step_start is not null
connect by level <= (step_end - step_start) * 60 * 24 + 1
and prior step_start = step_start
and prior dbms_random.value is not null
order by min_start;

Which gives you:

MIN_START                                   STEP_AVERAGE
---------------- ---------------------------------------
2015-01-01 06:00                                      20
2015-01-01 06:01                                20.46875
2015-01-01 06:02                                 20.9375
2015-01-01 06:03                                 20.9375
2015-01-01 06:04                                  21.875
2015-01-01 06:05                                  21.875
2015-01-01 06:06                                  21.875
2015-01-01 06:07                                  21.875
2015-01-01 06:08                                   23.75
2015-01-01 06:09                                   23.75
...
2015-01-01 06:14                                   23.75
2015-01-01 06:15                                    27.5
2015-01-01 06:16                                    27.5
...
2015-01-01 06:29                                    27.5
2015-01-01 06:30                                      35
2015-01-01 06:31                                      35
...
2015-01-01 06:59                                      35
2015-01-01 07:00                                      50
2015-01-01 07:01                                 49.6875
2015-01-01 07:02                                 49.6875
2015-01-01 07:03                                  49.375
...
ID10T_ERROR

You can use this query and change the hours to minutes. It works with any interval and will not fail on daylight savings days if this is a problem for you in your area.

This query returns Time of Max and Time of min, you can remove all of this in the final select statement.

In Oracle 11g how do you time weight average data hourly between two dates?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!