【问题标题】:How to get a first and last value for each partition in a column using SQL如何使用 SQL 获取列中每个分区的第一个和最后一个值
【发布时间】:2020-11-26 13:05:56
【问题描述】:

我的数据集如下所示。

         ts                c1           c2               c3
2019-01-04T01:50:00.000Z    C   25.48801612854004   33.317527770996094
2019-01-04T01:51:00.000Z    C   25.74610710144043   33.392295837402344
2019-01-04T01:52:00.000Z    C   25.978872299194336  33.29177474975586
2019-01-04T01:53:00.000Z    B   26.12158203125      33.2805061340332
2019-01-04T01:54:00.000Z    B   26.28511619567871   33.26923751831055
2019-01-04T01:55:00.000Z    C   26.470335006713867  33.25796890258789
2019-01-04T01:56:00.000Z    C   26.63957977294922   33.24669647216797
2019-01-04T01:57:00.000Z    C   26.954004287719727  33.23542785644531
2019-01-04T01:58:00.000Z    C   27.08258056640625   33.224159240722656
2019-01-04T01:59:00.000Z    A   27.25551986694336   33.212890625
2019-01-04T02:00:00.000Z    A   27.514263153076172  33.201622009277344
2019-01-04T02:01:00.000Z    A   27.588970184326172  33.17148971557617
2019-01-04T02:02:00.000Z    B   27.727638244628906  33.13819122314453
2019-01-04T02:03:00.000Z    B   27.956039428710938  33.104896545410156
2019-01-04T02:04:00.000Z    B   28.152463912963867  33.10499954223633

我想为“c1”列中的每个分区值获取“ts”的第一个和最后一个值。 我已经尝试了以下查询,但它没有返回正确的结果。

SELECT ts, c1, c2, c3,
first_value(ts) OVER (partition by c1 order by ts
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as first,
last_value(ts) OVER (partition by c1 order by ts
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as last
FROM `default`.`a07_a15`

问题:第一个值只返回三个不同的 ts 值,而最大值返回完全错误。

预期:我需要每个重复分区值的第一个和最后一个值。

         ts                c1           c2               c3                 first                last
2019-01-04T01:50:00.000Z    C   25.48801612854004   33.317527770996094  2019-01-04T01:50:00.000Z    2019-01-04T01:52:00.000Z
2019-01-04T01:51:00.000Z    C   25.74610710144043   33.392295837402344  2019-01-04T01:50:00.000Z    2019-01-04T01:52:00.000Z
2019-01-04T01:52:00.000Z    C   25.978872299194336  33.29177474975586   2019-01-04T01:50:00.000Z    2019-01-04T01:52:00.000Z
2019-01-04T01:53:00.000Z    B   26.12158203125      33.2805061340332    2019-01-04T01:53:00.000Z    2019-01-04T01:54:00.000Z
2019-01-04T01:54:00.000Z    B   26.28511619567871   33.26923751831055   2019-01-04T01:53:00.000Z    2019-01-04T01:54:00.000Z
2019-01-04T01:55:00.000Z    C   26.470335006713867  33.25796890258789   2019-01-04T01:55:00.000Z    2019-01-04T01:58:00.000Z
2019-01-04T01:56:00.000Z    C   26.63957977294922   33.24669647216797   2019-01-04T01:55:00.000Z    2019-01-04T01:58:00.000Z
2019-01-04T01:57:00.000Z    C   26.954004287719727  33.23542785644531   2019-01-04T01:55:00.000Z    2019-01-04T01:58:00.000Z    
2019-01-04T01:58:00.000Z    C   27.08258056640625   33.224159240722656  2019-01-04T01:55:00.000Z    2019-01-04T01:58:00.000Z
2019-01-04T01:59:00.000Z    A   27.25551986694336   33.212890625        2019-01-04T01:59:00.000Z    2019-01-04T02:01:00.000Z
2019-01-04T02:00:00.000Z    A   27.514263153076172  33.201622009277344  2019-01-04T01:59:00.000Z    2019-01-04T02:01:00.000Z
2019-01-04T02:01:00.000Z    A   27.588970184326172  33.17148971557617   2019-01-04T01:59:00.000Z    2019-01-04T02:01:00.000Z
2019-01-04T02:02:00.000Z    B   27.727638244628906  33.13819122314453   2019-01-04T02:02:00.000Z    2019-01-04T02:04:00.000Z
2019-01-04T02:03:00.000Z    B   27.956039428710938  33.104896545410156  2019-01-04T02:02:00.000Z    2019-01-04T02:04:00.000Z
2019-01-04T02:04:00.000Z    B   28.152463912963867  33.10499954223633   2019-01-04T02:02:00.000Z    2019-01-04T02:04:00.000Z

【问题讨论】:

    标签: sql pyspark apache-spark-sql hiveql window-functions


    【解决方案1】:

    使用lag()lead()

    select t.*
    from (select t.*,
                 lag(c1) over (order by ts) as prev_c1,
                 lead(c1) over (order by ts) as next_c1
          from t
         ) t
    where prev_c1 is null or next_c1 is null or
          prev_c1 <> c1 or next_c1 <> c1;
    

    这会将值放在不同的行中。如果您希望它们在同一行中,可能将其视为间隙和孤岛问题是最简单的解决方案:

    select c1, min(ts), max(ts)
    from (select t.*,
                 row_number() over (order by ts) as seqnum,
                 row_number() over (partition by c1 order by ts) as seqnum_2
          from t
         ) t
    group by c1, (seqnum - seqnum_2);
    

    编辑:

    如果您需要保留原始行,只需使用窗口函数:

    select t.*,
           min(ts) over (partition by c1, (seqnum - seqnum2)) as min_ts,
           max(ts) over (partition by c1, (seqnum - seqnum2)) as max_ts
    from (select t.*,
                 row_number() over (order by ts) as seqnum,
                 row_number() over (partition by c1 order by ts) as seqnum_2
          from t
         ) t
    

    【讨论】:

    • @Linoff 上述解决方案工作正常(第二个)。但我想包括数据集中所有可用的列(ts、c1、c2 和 c3)。假设我们在第一个分区中有 4 行,我们需要为 4 行放置相同的常量值(第一个和最后一个),依此类推
    • @Sudha 。 . .第一个保留第一行和最后一行的所有列。
    • @Linoff 抱歉,我试过了,但没有按预期工作。我们需要“ts”值的最小值和最大值,但它返回分区值。请检查我在上述帖子中编辑的预期输出。
    • @Sudha 。 . .这确实是对查询的调整。您不需要聚合,只需要窗口函数。
    • 太棒了!现在它按预期工作。非常感谢@Linoff。
    猜你喜欢
    • 1970-01-01
    • 2019-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 2016-12-12
    相关资源
    最近更新 更多