寻找许多 Oracle 时间戳之间的平均值?

Finding average between many Oracle timestamps?

所以我正在尝试使用 Oracle 查找时间戳列表的平均值。我有一个为 itemX 增长的 table。每次调用 itemX 时,它都会将一个值压入我的 table。这可能是几天、几个月、几年的数据和时间戳。我所关心的只是最后 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 秒左右。

您可以使用LAG/LEAD解析函数求出previous/next值,然后减去一个区间并提取组成部分和平均值:

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

这输出:

ITEMX | AVERAGE_SECONDS_DIFFERENCE
:---- | -------------------------:
A1    |                  61.000001

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

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

db<>fiddle here

与@MTO 的基本思想相同,但这使用您的原始查询 - 包括 filter/limit - 在 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

我仍然发布这个的唯一原因是它的行为因应用 filter/limit 的位置而不同。这是查看最近 10 个时间戳之间的 9 个间隔的平均值(如果过去 3 小时内有那么多时间戳)。如果你在末尾应用 filter/limit 那么它将包括 10 号和 11 号之间的间隔,即使 11 号早很多小时也是如此。

当然是看你自己了,从问题上看还不是很清楚。