如何在oracle中的两个区间之间分配平均值

How to spread the average between two intervals in oracle

如果给出一年中每个日期 24 小时的平均值。我想将这个每小时平均值分布到每分钟的平均值。 例如给出

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

我希望输出计算如下....

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

想法是得到两个区间之间的平滑上升或下降图。在输出中,您可以看到当我们从 20->50

移动时,平均值随着分钟数的增加而逐渐增加

这可以使用 Oracle 查询或一些 PL/SQL 代码实现吗?

您可以使用 recursive subquery factoring 进行间隔减半,并为每个步骤找到加权平均值(或此计算应该找到的任何值):

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;

主播成员通过lead()获取初始半小时时段和下一时段的平均值,并使用它们来计算初始的(20+50)/2等:

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

递归成员然后重复该过程,但使用上一步的周期长度和计算的平均值。我现在让它在这段时间的最后一分钟停止。

这样就得到了中间结果集:

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

然后您可以使用另一个递归 CTE,或者我认为更简单的 connect by 子句,将每个步骤扩展为适当的分钟数,每个步骤具有相同的 'average' 值:

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;

这给你:

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

编辑:添加并集以包含最后缺失的行

像这样的事情可能会奏效。假设输入数据在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

您可以使用此查询并将小时更改为分钟。它适用于任何时间间隔,如果您所在地区遇到问题,它不会在夏令时失败。

此查询 returns Time of Max 和 Time of min,您可以在最后的 select 语句中删除所有这些。