【问题标题】:Finding average between many Oracle timestamps?在许多 Oracle 时间戳之间找到平均值?
【发布时间】:2020-03-23 16:00:29
【问题描述】:

所以我正在尝试使用 Oracle 查找时间戳列表的平均值。我有一个为 itemX 增长的表。每次调用 itemX 时,它都会将一个值推送到我的表中。这可能是几天、几个月、几年的数据和时间戳。我所关心的只是最后 10 个时间戳的平均值,而且只有在过去 3 个小时内才会出现。

我的数据看起来像这样......

ROW_NUM itemX   DEVICE_TIMESTAMP
1   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:46:51.000000 PM
2   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:45:50.000000 PM
3   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:44:49.000000 PM
4   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:43:49.000000 PM
5   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:42:49.000000 PM
6   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:41:48.000000 PM
7   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:40:47.000000 PM
8   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:39:46.000000 PM
9   9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:38:45.000000 PM
10  9094E4E56CAEF8D7E0531965000A285C    3/23/2020 12:37:44.000000 PM

使用:

select row_number() over(order by device_timestamp desc) row_num, 
                itemX, device_timestamp 
            from  tracks_report 
            where device_timestamp >= sys_extract_utc(systimestamp) - INTERVAL '03:00' HOUR TO MINUTE 
            and itemX = '9094E4E56CAEF8D7E0531965000A285C'
            order by device_timestamp desc
            FETCH NEXT 10 ROWS ONLY

我想要得到的是这 10 行之间的平均时间。我尝试将其分解为秒和分钟,将它们相加除以 10,然后求平均值。但我的价值观不正确。这将是一个函数,我可以根据 itemX id 调用它。

有什么建议吗?我应该得到大约 60 秒的时间。但是我的崩溃和平均结果只有大约 47 秒。

【问题讨论】:

  • 平均时间戳是多少?如果只有两个,那是否是“中间”的时间戳,即两个输入时间戳的间隔绝对值相等的时间戳?您期望该样本数据会产生什么结果?
  • 对不起,我正在寻找几秒钟内的结果。因此,如果每条记录距最后一条记录 61 秒,那么我会寻找 61 秒。提供的数据非常接近,实时从一个传入记录到下一个记录可能会有几分钟的延迟。

标签: oracle timestamp average


【解决方案1】:

您可以使用LAG/LEAD解析函数找到上一个/下一个值,然后相减得到一个区间并提取组成部分和平均值:

SELECT itemx,
       AVG(
         EXTRACT( HOUR   FROM diff_since_last ) * 3600
       + EXTRACT( MINUTE FROM diff_since_last ) * 60 
       + EXTRACT( SECOND FROM diff_since_last )
       ) AS average_seconds_difference
FROM   (
  SELECT ROW_NUMBER() OVER ( PARTITION BY itemx ORDER BY device_timestamp DESC )
           AS rn,
         itemx,
         device_timestamp,
         device_timestamp
           - LEAD( device_timestamp )
             OVER ( PARTITION BY itemx ORDER BY device_timestamp DESC )
             AS diff_since_last
  FROM   tracks_report t
) t
WHERE  rn <= 10
AND    FROM_TZ( device_timestamp, 'UTC' ) >= SYSTIMESTAMP - INTERVAL '3' HOUR
GROUP BY itemx

对于测试数据:

CREATE TABLE tracks_report ( itemX, DEVICE_TIMESTAMP ) AS
SELECT 'A1',
        CAST( TRUNC( SYSTIMESTAMP, 'HH' ) AS TIMESTAMP )
          + INTERVAL '1:01.000001' MINUTE TO SECOND * ( LEVEL - 1 )
FROM   DUAL
CONNECT BY LEVEL <= 20

这个输出:

项目 | AVERAGE_SECONDS_DIFFERENCE :---- | -------------------------: A1 | 61.000001

(注意:平均值包括小数秒,我认为这很重要,因为您使用的是 TIMESTAMP 数据类型而不是 DATE 数据类型。)

(注 2:这是从最近的 10 个时间戳到前一个时间戳的间隔的平均值;所以它会考虑从第 10 个到第 11 个最近的时间戳的间隔,即使第 11 个时间戳在 3 小时之外范围和第 10 个在 3 小时内。如果您只想考虑所有值何时在该 3 小时范围内,则将过滤器从外部查询移动到内部查询。如果您想比较两者之间的 9 个间隔10 个值 [而不是 11 个值之间的 10 个间隔] 然后更改为 rn &lt;= 9。)

db小提琴here

【讨论】:

  • 谢谢。我一直把头撞在墙上,不考虑铅。我很欣赏这些信息。完美运行。
【解决方案2】:

与@MTO 的基本思想相同,但它在 CTE 中使用您的原始查询 - 包括过滤器/限制:

with cte1 (row_num, itemx, device_timestamp) as (
  select row_number() over(order by device_timestamp desc), 
    itemX,
    device_timestamp
  from tracks_report 
  where device_timestamp >= sys_extract_utc(systimestamp) - INTERVAL '03:00' HOUR TO MINUTE 
  and itemX = '9094E4E56CAEF8D7E0531965000A285C'
  order by device_timestamp desc
  FETCH NEXT 10 ROWS ONLY
)
select row_num,
  itemX,
  device_timestamp,
  device_timestamp
    - lead(device_timestamp) over (partition by itemX order by device_timestamp desc)
    as diff_interval
from cte1;

然后您可以使用extract() 获得间隔(以秒为单位)的差异:

with cte1 (row_num, itemx, device_timestamp) as (
...
),
cte2 (row_num, itemX, device_timestamp, diff_interval) as (
  select row_num,
    itemX,
    device_timestamp,
    device_timestamp
      - lead(device_timestamp) over (partition by itemX order by device_timestamp desc)
      as diff_interval
  from cte1
)
select row_num, itemX, device_timestamp, diff_interval,
  extract(hour from diff_interval) * 3600
    + extract(minute from diff_interval) * 60
    + extract(second from diff_interval) as diff_seconds
from cte2;

而不是全部显示,取平均值:

with cte1 (row_num, itemx, device_timestamp) as (
...
),
cte2 (row_num, itemX, device_timestamp, diff_interval) as (
...
)
select avg(
    extract(hour from diff_interval) * 3600
      + extract(minute from diff_interval) * 60
      + extract(second from diff_interval)
  ) as avg_diff_seconds
from cte2;

AVG_DIFF_SECONDS
----------------
      60.7777778

我仍然发布此消息的唯一原因是它的行为因应用过滤器/限制的位置而异。这是查看最近 10 个时间戳之间的 9 个间隔的平均值(如果过去 3 小时内有那么多)。如果您在最后应用过滤器/限制,那么它将包括 10 日和 11 日之间的时间间隔,即使 11 日早了很多小时。

当然,哪个是正确的取决于你,从问题中并不完全清楚。

【讨论】:

  • @MTO - 是的,但这取决于您是否想要平均 9 或 10 个间隙。第 10 个间隙显示为空, avg 忽略。根据 OP 的数据,1:01 有 7 个缺口,1:00 有两个缺口,即 547,这 9 个缺口的平均值为 60.777……如果第 11 条记录早得多,则第 10 条缺口可能会倾斜很多。就像我说的,目前还不清楚需要什么。 (所以我已经赞成你的答案*8-)
  • 感谢您的信息。我将不得不考虑哪个会更有效。要求来自一个非技术人员,他随机说抓取最后十个时间戳并给我平均值,前提是它们是在最后 3 小时内出现的。所以,我将不得不考虑两者。我认为“时间戳的数量”不如过去 3 小时内那么重要。但数字越大,差距可能越大。感谢您的信息。
  • 模棱两可的要求总是很有趣 *8-) 我可能会模拟一些清楚地表明差异的数据 - 例如你最近的 10 个值之间有小的差距,然后到第 11 个有很大的差距;运行查询以显示您可以获得的差距和两个答案;并询问非技术人员哪一个对他们更有意义。
  • 这是个好主意。再次感谢您提供的信息。
猜你喜欢
  • 1970-01-01
  • 2017-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-11
  • 1970-01-01
相关资源
最近更新 更多