我有这样一张桌子:

start_dt end_dt ID Index
2023-01-01 2023-03-08 A i
2023-05-08 2023-06-18 A i+1
2023-12-09 2024-02-02 A i+2
2024-12-01 2025-01-05 A i+3
2023-02-01 2023-03-07 B j
2023-06-08 2023-07-18 B j+1
2024-12-09 2024-02-02 B j+2

我需要创建一个满足条件的新ID(分别针对每个ID): 如果Start_dt[i+1]-end_dt[i]<9个月,则行i和i+1折叠,Start_dt_new=Start_dt[i],end_dt_new=end_dt[i+1].

因此,生成的表将如下所示

start_dt end_dt ID
2023-01-01 2024-02-02 A
2024-12-01 2025-01-05 A
2023-02-01 2023-07-18 B
2024-12-09 2024-02-02 B

我怎样才能用PostgreSQL做到这一点?

我try 了什么:

create table test (
    
    start_dt date,
    end_dt date,
    id varchar(10)
    
);


insert into test(start_dt, end_dt, id)
    values (date'2023-01-01', date'2023-03-08', 'A'),
    (date'2023-05-08', date'2023-06-18', 'A'),
    (date'2023-12-09', date'2024-02-02', 'A'),
    (date'2024-12-01', date'2025-01-05', 'A'),
    (date'2023-02-01', date'2023-03-07', 'B'),
    (date'2023-06-08', date'2023-07-18', 'B'),
    (date'2024-12-09', date'2024-02-02', 'B');
    
    with a as (
      select a.*,
      case when age(lead(start_dt) over(partition by id order by start_dt), end_dt)
      < interval'9 months' then 1 else 0 end monotony_f
      from test a
      order by id, start_dt
    ),
    b as (
      select start_dt, end_dt, id, coalesce(lag(monotony_f) over(partition by id order by start_dt), 1) monotony_f
      from a
      order by id, start_dt
    )
    select start_dt, end_dt, id from b
    where monotony_f = 0
    union
    select min(start_dt) start_dt, max(end_dt) end_dt, id from b
    where monotony_f = 1
    group by id
    order by id, start_dt

但或许还有更正确、更自然的方法呢?我的解决方案不包括这样的例子:

start_dt end_dt ID Index
2023-01-01 2023-03-08 A i
2023-05-08 2023-06-18 A i+1
2023-12-09 2024-02-02 A i+2
2024-12-01 2025-01-05 A i+3
2025-11-01 2025-12-07 A i+4
2026-01-08 2026-07-18 A i+5

推荐答案

例如

with a as (
  select a.*
    ,case when age(start_dt,lag(end_dt,1,start_dt)over(partition by id order by start_dt))
            < interval'9 months' 
     then 0 
     else 1 -- start new group
     end monotony_f
  from test a
  order by id, start_dt
),
b as (
  select a.*
      -- group number
     ,sum(monotony_f)over(partition by id order by start_dt) monotony_grn
  from a
)
-- collapse group to one row
select min(start_dt) start_dt,max(end_dt) end_dt,id
from b
group by id,monotony_grn
order by id,min(start_dt);

分组前

start_dt end_dt id age monotony_f monotony_grn
2023-01-01 2023-03-08 A 00:00:00 0 0
2023-05-08 2023-06-18 A 2 mons 0 0
2023-12-09 2024-02-02 A 5 mons 21 days 0 0
2024-12-01 2025-01-05 A 9 mons 28 days 1 1
2023-02-01 2023-03-07 B 00:00:00 0 0
2023-06-08 2023-07-18 B 3 mons 1 day 0 0
2024-12-09 2024-02-02 B 1 year 4 mons 22 days 1 1
2023-01-01 2023-03-08 C 00:00:00 0 0
2023-05-08 2023-06-18 C 2 mons 0 0
2023-12-09 2024-02-02 C 5 mons 21 days 0 0
2024-12-01 2025-01-05 C 9 mons 28 days 1 1
2025-11-01 2025-12-07 C 9 mons 27 days 1 2
2026-01-08 2026-07-18 C 1 mon 1 day 0 2

Sql相关问答推荐

如何根据SQL中的列条件获取下一个时间戳?

如何实现一个广泛的多级自连接PostgreSQL查询?

在SQL中使用类别值将行转置为列

连接三个表的正确方式是什么?在这三个表中,可以显示在一个表上的行将在其他表中显示结果

查找表中特定值的上次更新日期

导出部分条形码字符串GS1-128

SQL:如何在表中同时使用GROUPING和CONDITION?

如何将我的联接数据放入每个用户每月多行的列中?

如何使用SQL生成数据的滚动3天总和

从另一个没有公共键的表中获取值来加入

AdventureWorks 查询

在 SQL Server 中查找重复项

Oracle 21c 中的递归查询回顾过go 3 周

INSERT INTO 语法

如何在 SQL 中将两行(或多行)jsonb 数组合并为一行

Postgres存在限制问题「小值」

在Snowflake中,如何将以逗号和连字符分隔的多个混合数值拆分成数字列表

如何通过CROSS APPLY获取多级嵌套JSON属性的值?

面对来自以下两个代码的不同输出

SQL:有没有办法根据另一列的数据细节过滤和形成另一列?