How to spread the average between two intervals in oracle

纵然是瞬间 提交于 2019-12-21 06:01:08

问题


If given the Average for 24 hours for each date in a year. I want to spread this hourly average to average at each minute. e.g. given

Date        Time    Average
01-Jan-15   23:00   20
02-Jan-15   00:00   50
02-Jan-15   01:00   30

I want the output to be calculated something as below ....

DateTime              AVG_VALUE
01/01/2015 23:00:00   20
01/01/2015 23:01:00   20.5
01/01/2015 23:02:00   21
01/01/2015 23:03:00   21.5
01/01/2015 23:04:00   22
01/01/2015 23:05:00   22.5
01/01/2015 23:06:00   23
01/01/2015 23:07:00   23.5
01/01/2015 23:08:00   24
01/01/2015 23:09:00   24.5
01/01/2015 23:10:00   25
01/01/2015 23:11:00   25.5
01/01/2015 23:12:00   26
01/01/2015 23:13:00   26.5
01/01/2015 23:14:00   27
01/01/2015 23:15:00   27.5
01/01/2015 23:16:00   28
01/01/2015 23:17:00   28.5
01/01/2015 23:18:00   29
01/01/2015 23:19:00   29.5
01/01/2015 23:20:00   30
01/01/2015 23:21:00   30.5
01/01/2015 23:22:00   31
01/01/2015 23:23:00   31.5
01/01/2015 23:24:00   32
01/01/2015 23:25:00   32.5
01/01/2015 23:26:00   33
01/01/2015 23:27:00   33.5
01/01/2015 23:28:00   34
01/01/2015 23:29:00   34.5
01/01/2015 23:30:00   35
01/01/2015 23:31:00   35.5
01/01/2015 23:32:00   36
01/01/2015 23:33:00   36.5
01/01/2015 23:34:00   37
01/01/2015 23:35:00   37.5
01/01/2015 23:36:00   38
01/01/2015 23:37:00   38.5
01/01/2015 23:38:00   39
01/01/2015 23:39:00   39.5
01/01/2015 23:40:00   40
01/01/2015 23:41:00   40.5
01/01/2015 23:42:00   41
01/01/2015 23:43:00   41.5
01/01/2015 23:44:00   42
01/01/2015 23:45:00   42.5
01/01/2015 23:46:00   43
01/01/2015 23:47:00   43.5
01/01/2015 23:48:00   44
01/01/2015 23:49:00   44.5
01/01/2015 23:50:00   45
01/01/2015 23:51:00   45.5
01/01/2015 23:52:00   46
01/01/2015 23:53:00   46.5
01/01/2015 23:54:00   47
01/01/2015 23:55:00   47.5
01/01/2015 23:56:00   48
01/01/2015 23:57:00   48.5
01/01/2015 23:58:00   49
01/01/2015 23:59:00   49.5
02/01/2015            50
02/01/2015 00:01:00   49.66666667
02/01/2015 00:02:00   49.33333333
02/01/2015 00:03:00   49
02/01/2015 00:04:00   48.66666667
02/01/2015 00:05:00   48.33333333

The idea is to get the smooth incline or decline graph between two interval. In the output you can see the avg is gradually increasing with increasing minutes as we move from 20->50

Can this be achived using Oracle Query or some PL/SQL code?


回答1:


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



回答2:


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
...



回答3:


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?



来源:https://stackoverflow.com/questions/31541384/how-to-spread-the-average-between-two-intervals-in-oracle

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