【问题标题】:How can I perform linear interpolation using oracle SQL?如何使用 oracle SQL 执行线性插值?
【发布时间】:2011-09-09 19:05:56
【问题描述】:

我正在尝试使用 Oracle 11g(开发中的 11.1,生产中的 11.2)进行数值分析,特别是对具有三列感兴趣的表的线性插值:时间戳、设备 ID 和值。

值列保存来自设备的数据(id 为 deviceid),在时间戳中给定的时间获取。例如,这是伪造的数据,但它给出了这个想法:

     time       |  deviceid  |  value   
----------------|------------|-----------
 01:00:00.000   |  001       | 1.000
 01:00:01.000   |  001       | 1.030
 01:00:02.000   |  001       | 1.063 
 01:00:00.050   |  002       | 553.10
 01:00:01.355   |  002       | 552.30
 01:00:02.155   |  002       | 552.43 

设备 001 的时间戳与设备 002 的时间戳不匹配,但我需要将设备 001 和 002 的值放在一行中,并带有一个时间戳,与设备 001 的时间戳匹配。我想要结束的内容up with 是这样的:

     time       |  device 001  |  device 002   
----------------|--------------|------------
 01:00:00.000   |  1.000       |  null
 01:00:01.000   |  1.030       |  552.520
 01:00:02.000   |  1.063       |  552.405

其中设备 002 的值是根据在设备 001 的每个时间戳两侧最近的两个时间戳收集的设备 002 的值进行线性插值的。 出现空值是因为我在 01:00:00.000 的任一侧都没有设备 002 的两个时间戳,并且我不想推断该值。

据我了解,我可以使用 percentile_cont 来做到这一点,但我不明白我在网上看到的例子。例如, percentile_cont 使用的百分位数来自哪里?

提前感谢您的帮助!

【问题讨论】:

  • 感谢您到目前为止的回答。我对 Oracle 分析函数非常陌生,我的数据不像我给出的示例那么整齐。例如,对于 device2 上的每个值,device1 都有多个值。对于一个设备,这些值以大约十分之一秒的速度递增,而对于另一个设备,这些值以三分之二的时间递增。编辑问题以提供更多详细信息...

标签: sql oracle analytic-functions linear-interpolation


【解决方案1】:

我不确定您将如何使用 PERCENTILE_CONT 进行您要求的插值,但借助不同的分析函数,您可以实现您想要的。

首先,我们将创建以下函数,它将INTERVAL DAY TO SECOND 的值转换为秒:

CREATE OR REPLACE FUNCTION intvl_to_seconds(
    p_interval INTERVAL DAY TO SECOND
) RETURN NUMBER DETERMINISTIC
AS
BEGIN
  RETURN EXTRACT(DAY FROM p_interval) * 24*60*60
       + EXTRACT(HOUR FROM p_interval) * 60*60
       + EXTRACT(MINUTE FROM p_interval) * 60
       + EXTRACT(SECOND FROM p_interval);
END;
/

通过这个函数,我们可以使用如下查询:

SELECT d1.time,
       d1.value AS value1,
       q2.prev_value + intvl_to_seconds(d1.time - q2.prev_time) * (q2.next_value - q2.prev_value)/intvl_to_seconds(q2.next_time - q2.prev_time) AS value2
  FROM devices d1
  LEFT OUTER JOIN (SELECT d2.time AS prev_time,
                          d2.value AS prev_value,
                          LEAD(d2.time, 1) OVER (ORDER BY d2.time) AS next_time,
                          LEAD(d2.value, 1) OVER (ORDER BY d2.time) AS next_value
                     FROM devices d2
                    WHERE d2.deviceid = 2) q2
               ON d1.time BETWEEN q2.prev_time AND q2.next_time
 WHERE d1.deviceid = 1;

我在上面获取了你的数据,将时间戳的日期部分设置为今天,当我运行上面的查询时,我得到了以下结果:

TO_CHAR(D1.TIME) VALUE1 VALUE2 ------------------------------------- ---------- --- -------- 11 年 9 月 9 日 01.00.00.000000 1 11 年 9 月 9 日 01.00.01.000000 1.03 552.517625 11 年 9 月 9 日 01.00.02.000000 1.063 552.404813

(我在d1.time 周围添加了一个TO_CHAR 以减少SQL*Plus 中过多的间距。)

如果您使用DATEs 而不是TIMESTAMPs,则不需要该函数:只需减去日期即可。

【讨论】:

  • 编写函数的另一种方法是使用CAST(timestamp_column AS DATE)。这可以避免大量 SQL 到 PL/SQL 并返回上下文切换。
  • ...不,我不能。如果我投射到日期,我会丢失小数秒数据。
  • 自 1970 年 1 月 1 日以来,我一直在努力尝试将时间戳转换为毫秒数;我实际上没有在数据库上创建函数的权限。
  • @GLaDOS:我使用了一个函数来使查询看起来更整洁一些。如果您没有创建函数的权限,则可以将函数调用替换为对 EXTRACT 的各种调用。
  • @GLaDOS:我使用 LEFT OUTER JOIN 生成空值,就像您预期的输出数据的第一行一样,因为我认为这就是您想要的。
【解决方案2】:

我正在使用@Luke Woodward 查询的修改版本:

SELECT d1.time,
   d1.value AS value1,
   q2.prev_value + 
   (EXTRACT( SECOND FROM (d1.time - q2.prev_time)) +
    EXTRACT( MINUTE FROM (d1.time - q2.prev_time)) * 60 ) 
    * (q2.next_value - q2.prev_value)/
      (EXTRACT ( SECOND FROM (q2.next_time - q2.prev_time)) + 
      EXTRACT ( MINUTE FROM (q2.next_time - q2.prev_time)) * 60)  AS value2
FROM devices d1
LEFT OUTER JOIN (SELECT d2.time AS prev_time,
                      d2.value AS prev_value,
                      LEAD(d2.time, 1) OVER (ORDER BY d2.time) AS next_time,
                      LEAD(d2.value, 1) OVER (ORDER BY d2.time) AS next_value
                 FROM devices d2
                WHERE d2.deviceid = 2
                      and time between '20100914 000000' and '20100915 000000'
                ) q2
           ON d1.time BETWEEN q2.prev_time AND q2.next_time
 WHERE d1.deviceid = 1;

但插值始终显示为空,即使日期范围内有设备 2 的数据。

注意,我必须在 q2 中为查询添加日期范围,这可能是普通连接丢失外部数据的原因。

如果我使用普通连接,我不会得到插值数据的空值,但是在使用普通连接时,我会丢失设备 2 端点之外的设备 1 的数据(q2 中的插值设备)。有什么建议吗?

【讨论】:

  • 注意,见上面的最终解决方案。再次感谢@Luke Woodward
  • 下面的最终解决方案...订单已切换...可能会再次切换...所以我使用的最终解决方案是“最终解决方案...”
【解决方案3】:

带日期范围的最终解决方案:

SELECT
    d1.time,
    d1.value AS value1,
    q2.prev_value + 
    (EXTRACT( SECOND FROM (d1.time - q2.prev_time)) +
     EXTRACT( MINUTE FROM (d1.time - q2.prev_time)) * 60 ) 
     * (q2.next_value - q2.prev_value)/
       (EXTRACT ( SECOND FROM (q2.next_time - q2.prev_time)) + 
        EXTRACT ( MINUTE FROM (q2.next_time - q2.prev_time)) * 60
    )  AS value2
FROM devices d1
LEFT OUTER JOIN (
    SELECT d2.time AS prev_time,
           d2.value AS prev_value,
           LEAD(d2.time, 1) OVER (ORDER BY d2.time) AS next_time,
           LEAD(d2.value, 1) OVER (ORDER BY d2.time) AS next_value
    FROM devices d2
    WHERE d2.deviceid = 2
    AND time BETWEEN '20100914 000000' AND '20100915 000000'
) q2
ON d1.time BETWEEN q2.prev_time AND q2.next_time
WHERE d1.deviceid = 1
AND time BETWEEN '20100914 000000' AND '20100915 000000';

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-22
    • 2012-02-08
    • 2012-01-29
    • 2016-09-20
    • 2016-11-03
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    相关资源
    最近更新 更多