基于顺序和键的sql分组行

az31mfrm  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(361)

我有一张表格,里面有一组临时工,他们的合同可以续签,他们可以在一段重叠的时间内继续工作,也可以稍后再回来为不同的部门或同一个部门工作。我的目标是以这样一种方式合并数据行:如果工作时间重叠,那么实际上重叠的时段可以转换为一行。如果没有,则应将它们表示为单独的行。
电流输出:

+---------------------+------------------+------------------+------------------+
|         Name        |    Department    |    Start Date    |     End Date     |
+---------------------+------------------+------------------+------------------+
|         Tom         |     Finance      |    2010-08-09    |   2010-09-09     |
|         Tom         |     Finance      |    2010-09-10    |   2010-10-10     |
|         Tom         |        HR        |    2010-11-01    |   2011-01-15     |
|         Tom         |     Finance      |    2011-02-01    |   2011-03-01     |
+---------------------+------------------+------------------+------------------+

期望输出

+---------------------+------------------+------------------+------------------+
|         Name        |    Department    |    Start Date    |     End Date     |
+---------------------+------------------+------------------+------------------+
|         Tom         |     Finance      |    2010-08-09    |   2010-10-10     |
|         Tom         |        HR        |    2010-11-01    |   2011-01-15     |
|         Tom         |     Finance      |    2011-02-01    |   2011-03-01     |
+---------------------+------------------+------------------+------------------+

我试着按姓名和部门分组,然后输出最小和最大日期。然而,这不适用于上述示例。
我还试图排名的顺序,但是这个函数不重置排名数字时,继续的价值不匹配。

idv4meu8

idv4meu81#

这是一个缺口和孤岛问题的例子。假设表中的时间框架没有间隔,最简单的方法可能是行号的差异:

select name, department, min(start_date), max(end_date)
from (select t.*,
             row_number() over (partition by name order by start_date) as seqnum,
             row_number() over (partition by name, department order by start_date) as seqnum_nd
      from t
     ) t
group by name, department, (seqnum - sequm_nd);

如何解释这一点有点棘手。但是,如果您查看子查询的结果,您可能会看到行号的差异如何标识具有相同部门的相邻记录。
如果你有差距——你想考虑到这一点——那么你可以使用更精确的版本。在此方法中,使用 lag() 获取上一个结束日期,并使用它创建一个标志来标识“孤岛”何时开始。剩下的只是聚合:

select name, department, min(start_date), max(end_date)
from (select t.*,
             sum(case when prev_end_date >= start_date - interval '1 day' then 1 else 0 end) over (partition by name order by start_date) as grp
      from (select t.*,
                   lag(end_date) over (partition by name, department order by start_date) as prev_end_date
            from t
           ) t
     ) t
group by name, department, grp;

请注意,这使用了众所周知依赖于数据库的日期运算符。确切的语法取决于您使用的数据库,因此代码可能需要调整。

相关问题