在postgres中合并日期范围以消除重叠

时间:2015-11-20 14:26:49

标签: postgresql date overlap date-range coalesce

如果我有一张postgres表a:

  member |    start   |    end
---------+------------+------------
    1    | 2015-01-01 | 2015-05-01
---------+------------+------------
    1    | 2015-03-01 | 2015-06-01
---------+------------+------------
    2    | 2015-01-01 | 2015-05-01
---------+------------+------------
    2    | 2015-06-01 | 2015-08-01

如何合并日期以消除重叠范围,如下所示:

  member |    start   |    end
---------+------------+------------
    1    | 2015-01-01 | 2015-06-01
---------+------------+------------
    2    | 2015-01-01 | 2015-05-01
---------+------------+------------
    2    | 2015-06-01 | 2015-08-01

1 个答案:

答案 0 :(得分:1)

chop CTE中,原始范围被“切割”成较小的,不相交的(但可能相邻的)范围。它们是从原始范围的所有端点构建的,包括开始和结束。

主要选择如下工作(从里到外读取):

  1. 范围的相邻标志在与先前范围相邻时为零(假设范围按其开始日期排序)。
  2. 相邻标志的累积总和为我们提供了一个分组值:所有相邻的范围将具有相同的总和。
  3. 最外面的块只是计算相邻范围组的边界值。
  4. window functions的黑魔法......

    with chop as (
      select member,
             pt as start,
             lead(pt) over (partition by member order by pt) finish,
             (
               select count(*)
               from   a
               where  b.member = a.member
               and    b.pt >= a.start
               and    b.pt < a.finish
             ) need_it
      from   (
               select member, start pt from a
               union
               select member, finish pt from a
             ) b
    )
    -- 3
    select member,
           min(start),
           max(finish)
    from   (
             -- 2
             select member,
                    start,
                    finish,
                    sum(adjacent) over (partition by member order by start) grp
             from   (
                      -- 1
                      select member,
                             start,
                             finish,
                             case
                               when start <= lag(finish) over (partition by member order by start)
                               then 0
                               else 1
                             end adjacent
                      from   chop
                      where  need_it > 0
                    ) t
           ) q
    group by member,
             grp
    order by member,
             min(start);
    

    我将end重命名为finish,因为end是关键字。