【问题标题】:Interpolating missing values in SQL在 SQL 中插入缺失值
【发布时间】:2020-01-15 15:36:49
【问题描述】:

我有一个跟踪手机电池电量和连接快照的数据集。但是,当手机断开连接时,它会报告与上次良好连接相同的数字,例如:

   id           timestamp connection bat_level
1   1 2019-09-12 20:00:00       TRUE         4
2   1 2019-09-12 20:05:00       TRUE         4
3   1 2019-09-12 20:10:00       TRUE         4
4   1 2019-09-12 20:15:00       TRUE         4
5   1 2019-09-12 20:20:00       TRUE         3
6   1 2019-09-12 20:25:00      FALSE         3
7   1 2019-09-12 20:30:00      FALSE         3
8   1 2019-09-12 20:35:00      FALSE         3
9   1 2019-09-12 20:40:00      FALSE         3
10  1 2019-09-12 20:45:00      FALSE         3
11  1 2019-09-12 20:50:00      FALSE         3
12  1 2019-09-12 20:55:00      FALSE         3
13  1 2019-09-12 21:00:00       TRUE         1
14  1 2019-09-12 21:05:00       TRUE         1
15  1 2019-09-12 21:10:00      FALSE         1
16  1 2019-09-12 21:15:00      FALSE         1
17  1 2019-09-12 21:20:00      FALSE         1
18  1 2019-09-12 21:25:00      FALSE         1
19  1 2019-09-12 21:30:00      FALSE         1
20  1 2019-09-12 21:35:00      FALSE         1
21  1 2019-09-12 21:40:00      FALSE         1
22  1 2019-09-12 21:45:00      FALSE         1
23  1 2019-09-12 21:50:00      FALSE         1
24  1 2019-09-12 21:55:00      FALSE         1
25  1 2019-09-12 22:00:00      FALSE         1

我的目标是创建第五列,它基本上插入了connection = FALSE 时发生的情况。我想从 last TRUE connection 的值开始以每分钟 0.05(不能低于 0)插入电池消耗率,因此输出将是:

   id           timestamp connection bat_level theoretical_bat_level
1   1 2019-09-12 20:00:00       TRUE         4                    NA
2   1 2019-09-12 20:05:00       TRUE         4                    NA
3   1 2019-09-12 20:10:00       TRUE         4                    NA
4   1 2019-09-12 20:15:00       TRUE         4                    NA
5   1 2019-09-12 20:20:00       TRUE         3                    NA
6   1 2019-09-12 20:25:00      FALSE         3                  2.75
7   1 2019-09-12 20:30:00      FALSE         3                  2.50
8   1 2019-09-12 20:35:00      FALSE         3                  2.25
9   1 2019-09-12 20:40:00      FALSE         3                  2.00
10  1 2019-09-12 20:45:00      FALSE         3                  1.75
11  1 2019-09-12 20:50:00      FALSE         3                  1.50
12  1 2019-09-12 20:55:00      FALSE         3                  1.25
13  1 2019-09-12 21:00:00       TRUE         1                    NA
14  1 2019-09-12 21:05:00       TRUE         1                    NA
15  1 2019-09-12 21:10:00      FALSE         1                  0.75
16  1 2019-09-12 21:15:00      FALSE         1                  0.50
17  1 2019-09-12 21:20:00      FALSE         1                  0.25
18  1 2019-09-12 21:25:00      FALSE         1                  0.00
19  1 2019-09-12 21:30:00      FALSE         1                  0.00
20  1 2019-09-12 21:35:00      FALSE         1                  0.00
21  1 2019-09-12 21:40:00      FALSE         1                  0.00
22  1 2019-09-12 21:45:00      FALSE         1                  0.00
23  1 2019-09-12 21:50:00      FALSE         1                  0.00
24  1 2019-09-12 21:55:00      FALSE         1                  0.00
25  1 2019-09-12 22:00:00      FALSE         1                  0.00

我知道应该使用带有lag() 的 cte,但不确定如何开始从每个 id 的最后一个 TRUE 值中减少 theoretical_bat_level

CASE WHEN connection = FALSE AND 
LAG(connection) OVER(PARTITION BY id, timestamp ASC) = True AND
connection = LAG(bat_level) OVER(PARTITION BY id, timestamp ASC) THEN  ?
WHERE connection = FALSE

任何关于正确使用方法的指导都会有所帮助

【问题讨论】:

    标签: sql postgresql presto


    【解决方案1】:

    我的解决方案需要一个以分钟为单位计算间隔长度的辅助函数:

    CREATE OR REPLACE FUNCTION int_minutes(interval) RETURNS double precision
       LANGUAGE sql STRICT IMMUTABLE AS
    'SELECT EXTRACT (minutes FROM $1)
           + 60 * extract (hours FROM $1)
           + 1440 * extract (days FROM $1)';
    

    然后你可以使用窗口函数来获取手机连接的最新时间:

    SELECT id, timestamp, connection, bat_level,
           greatest(bat_level
                    - int_minutes(timestamp
                                  - conn_ts_arr[cardinality(conn_ts_arr)]
                      ) * 0.05,
                    0.0
           ) AS theoretical_bat_level
    FROM (SELECT id, timestamp, connection, bat_level,
                 array_agg(timestamp)
                    FILTER (WHERE connection)
                    OVER (PARTITION BY id
                          ORDER BY timestamp) AS conn_ts_arr
          FROM cellbat) AS s1
    ORDER BY id, timestamp;
    

    【讨论】:

      【解决方案2】:

      您已经接近了,您需要将上次生成的值与 bat_level 合并,因此您只在第一次使用 bat 级别时

      像这样:

      CASE 
        WHEN connection = FALSE AND 
             LAG(connection) OVER(PARTITION BY id ORDER BY timestamp ASC) = True 
        THEN COALESCE(LAG(THEORETICAL_BAT_LEVEL) OVER(PARTITION BY id, timestamp ASC),
             LAG(bat_level) OVER(PARTITION BY id ORDER BY timestamp ASC)) - .25 
        ELSE NULL AS THEORETICAL_BAT_LEVEL
      

      【讨论】:

      • 谢谢,如果已经为 0 并且theoretical_bat_level 在表中不作为列存在,那么应该有一个条件阻止减法,那么如何在CASE WHEN 语句中引用它?
      • @the_darkside -- 好点已经在 0 -- lag 将在分区的第一行返回 null
      • 是否可以COALESCE() 一个尚不存在的列?
      • @Hogan。 . .您可以使用lag() 的三参数形式传入一个默认值。
      • 不确定我明白了,所以我应该在 COALESCE 语句中将 THEORETICAL_BAT_LEVEL 替换为 bat_level
      猜你喜欢
      • 1970-01-01
      • 2014-09-14
      • 2021-11-20
      • 1970-01-01
      • 1970-01-01
      • 2016-10-06
      • 2020-06-19
      • 1970-01-01
      • 2023-01-25
      相关资源
      最近更新 更多