使用 SQL 来处理某个瞬发的数据,想整理每行是每小时的输出的列表,如果当前小时没数据就用最后一次有数据的时间 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
uil330
V2EX    数据库

使用 SQL 来处理某个瞬发的数据,想整理每行是每小时的输出的列表,如果当前小时没数据就用最后一次有数据的时间

  •  
  •   uil330 2022-10-12 17:54:06 +08:00 2099 次点击
    这是一个创建于 1148 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如数据是这样子的:

    id data time
    1 0 00:03
    1 50 04:23
    1 20 07:03

    输出的表格是:

    id data time
    1 10 01:00
    1 10 02:00
    1 10 03:00
    1 10 04:00
    1 50 05:00
    1 50 06:00
    1 50 07:00
    1 20 08:00

    现在能做到用 join ,当前小时数据不存在的时候添加 0 进去:

    id data time
    1 10 01:00
    1 0 02:00
    1 0 03:00
    1 0 04:00
    1 50 05:00
    1 0 06:00
    1 0 07:00
    1 20 08:00

    但是我想要的是添加上一个值而不是 0 。

    求问怎么解?

    第 1 条附言    2022-10-12 18:34:55 +08:00
    找到方法了:
    1 先生成那个包含所有时间,对应时间不存在的项为 NULL 的表
    2 使用 last_value ,抽出最后一个非 null 的值

    具体的 SQL 我有时间补上

    不过这样的话感觉执行效率可能会低,请问有什么高效点的方法么
    12 条回复    2022-10-14 22:28:47 +08:00
    debuggerx
        1
    debuggerx  
       2022-10-12 18:33:30 +08:00
    这个活儿非要用 sql 实现?程序里处理那不就是一行的事
    uil330
        2
    uil330  
    OP
       2022-10-12 18:35:44 +08:00
    @debuggerx 1.50TB 放在 cloud 的数据我想不出比 sql 更有效率的方法了
    sun522198558
        3
    sun522198558  
       2022-10-12 19:13:51 +08:00
    一个变量记录下
    ```sql
    SET @prev = 0;
    SELECT
    IF
    ( `data` IS NOT NULL, @prev := `data`, @prev)
    ```
    zbinlin
        4
    zbinlin  
       2022-10-12 21:36:19 +08:00
    这里是每个小时只能有一条数据还是可以多条?如果第 1 个小时没有数据,那 data 是什么?
    wxf666
        5
    wxf666  
       2022-10-12 21:46:38 +08:00   1
    @sun522198558 对于 `MySQL`,官方不建议这样使用 *(两周前就有人 [发帖]( /t/883301 ) 说赋值异常的)*

    - 『在同一条语句内赋值和读取一个用户变量』的行为未定义
    - 『在 `SELECT` 中赋值用户变量』已被弃用,未来会移除


    @uil330 你是啥数据库啊?

    我看 `MySQL` 的 `LAG()`、`LAST_VALUE()` 等窗口函数,都还不支持 `IGNORE NULLS`

    `SQLite` 也还不支持在 `LAG()` 等窗口函数上使用 `FILTER (WHERE xxxIS NOT NULL)`,但能用在聚合函数上

    所以用 `SQLite` 写了试试:

    *( V 站排版原因,开头有全角空格。若要复制运行,记得删除)*

    ```sqlite
    WITH
      data(id, data, time) AS (
       VALUES
       (1, 10, '00:03'),
       (1, 50, '04:23'),
       (1, 20, '07:03')
     ),
     
      ids(id) AS (
       SELECT DISTINCT id
        FROM data
     ),
     
      time(hour) AS (
       SELECT value
        FROM json_each('[1, 2, 3, 4, 5, 6, 7, 8]')
     ),
     
      formated(id, hour, data) AS (
       SELECT id, strftime('%H', time) + 1, data
        FROM data
     )

    -- 按 id 分组,hour 为顺序,窗口范围为 [组内第一行, 当前行],并过滤掉 NULL 值,
    -- 剩余值合并成 json 数组,然后取数组最后一位(这个数组至多 24 个数嘛,不算大)
    SELECT id,
        json_group_array(data) FILTER(WHERE data IS NOT NULL) OVER win ->> '$[#-1]' data,
        format('%02d:00', hour) time
      FROM ids
      JOIN time
      LEFT JOIN formated USING(id, hour)
    WINDOW win AS (PARTITION BY id ORDER BY hour);
    ```
    shiyanfei5
        6
    shiyanfei5  
       2022-10-12 21:51:35 +08:00
    1.建议不要变为 0 ,变为 null
    2.思路:可通过 pg 语法
    2.1.select 里新增一个字段,使用 lag(data,1,-999999) over(partition by id order by time) 永远取上一条记录的值 为字段 last_val
    2.2 你已实现添加值为 0 ,此处建议把 0 改为 null ,case when 一下 如果 data 字段当前值为 null 或,则取 last_val ,否则就是当前值
    。。懒得写了,要么就上存储过程
    wxf666
        7
    wxf666  
       2022-10-12 23:17:04 +08:00
    @shiyanfei5 我查了查 `PostgreSQL` 关于窗口函数的 [文档]( https://www.postgresql.org/docs/current/functions-window.html ),pg 也不支持使用 `IGNORE NULLS` 取上一个非 `NULL` 值呀:

    > The SQL standard defines a RESPECT NULLS or IGNORE NULLS option for lead, lag, first_value, last_value, and nth_value. This is not implemented in PostgreSQL: the behavior is always the same as the standard's default, namely RESPECT NULLS

    你是咋做的呢?
    uil330
        8
    uil330  
    OP
       2022-10-13 08:55:33 +08:00
    @wxf666 用的是 bigquery

    sql 每个数据库支持的特性都不一样,真让人头大。。。
    sun522198558
        9
    sun522198558  
       2022-10-13 11:09:35 +08:00
    @wxf666 #5 你说这么多,新版本的 mysql 是不支持的窗口。
    只要当前版本能用就行
    sun522198558
        10
    sun522198558  
       2022-10-13 11:09:49 +08:00
    @sun522198558 #9 错了,是旧版本不支持
    wxf666
        11
    wxf666  
       2022-10-14 02:12:39 +08:00
    @uil330

    > 1.50TB 放在 cloud 的数据我想不出比 sql 更有效率的方法了

    这是要取回本地嘛?还是更新 /追加云端的数据?

    若取回本地的话,写个脚本应该会更快吧?*(至少,稀疏数据传得会快些?)*



    @sun522198558 数据库新手,好奇想问下,连 1~2 MB 的 `SQLite` 功能都开始丰富多样起来了,为啥还坚持旧版本 `MySQL` 呢?

    未定义行为,摸透了也确实能用 *(反正我没摸透,不知为啥那个帖子里的赋值是异常的)*

    不支持窗口函数,那就用基础 `SQL` 吧:*(感觉会比窗口函数慢)*

    ```sqlite
    WITH
      data(id, data, time) AS (
       VALUES
       (1, 10, '00:03'),
       (1, 50, '04:23'),
       (1, 20, '07:03')
     ),

      ids(id) AS (
       SELECT DISTINCT id
        FROM data
     ),

      time(time) AS (
       SELECT format('%02d:00', value)
        FROM json_each('[1, 2, 3, 4, 5, 6, 7, 8]')
     )

    -- 方法一
    SELECT i.id, t.time,
        (SELECT data
          FROM data
         WHERE id = i.id
          AND time < t.time
         ORDER BY time DESC
         LIMIT 1) data,
        NULL useless
      FROM ids i
      JOIN time t

    UNION ALL
    VALUES ('----', '----', '----', '----')
    UNION ALL

    -- 方法二
    SELECT i.id, t.time, d.data, MAX(d.time) useless
      FROM ids i
      JOIN time t
      LEFT JOIN data d ON i.id = d.id AND t.time > d.time
    GROUP BY i.id, t.time;
    ```
    shiyanfei5
        12
    shiyanfei5  
       2022-10-14 22:28:47 +08:00
    @wxf666 仔细看了一下,我写的思路还有点问题。 用 sum over()把,首先分区有序累加,按照 id 为粒度获取其累加的值。。原则上累加的值 如果相同且 id 相同,那么他们划分为同一个组里。
    然后只要 id 相同且在同一个组,由于其 data 为 0 ,所以直接 再 sum over 即可

    select
    id,
    data,
    time,
    sum(data) over( partition by id,group_sign order by time) as group_sign

    from
    (
    select
    id,
    data,
    time,
    sum(data) over( partition by id order by time) as group_sign
    from ta
    ) t1
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1253 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 17:16 PVG 01:16 LAX 09:16 JFK 12:16
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86