Oracle SQL - 需要帮助将一个日期值 EFF_DT 转换为具有唯一标准的日期范围 START_EFF_DT 和 END_EFF_DT

Oracle SQL - need help converting from one date value EFF_DT to a date range of START_EFF_DT and END_EFF_DT with unique criteria

我遇到了这个问题,我正在寻求有关此问题的任何帮助

背景: 我需要创建一个查询,根据我们 Oracle 数据库中可用的数据识别所有“无薪”工作日

我们的时间和劳动力跟踪数据仅包含 1 个代表“工作日期”的日期列 (EFF_DT)

原始 table 的简化版本如下所示:

EEID WORK_DT PAYCODE
1111 2022/03/01 UNPAID
1111 2022/03/02 REG_WORK
1111 2022/03/03 REG_WORK
1111 2022/03/04 UNPAID
1111 2022/03/05 UNPAID
1111 2022/03/06 SICK
1111 2022/03/07 SICK
1111 2022/03/08 UNPAID
1111 2022/03/09 UNPAID
1111 2022/03/10 UNPAID

我需要做的是仅过滤“UNPAID”行,然后我需要确定 start/end 无薪休假工作日期的“范围”

因此预期结果需要如下所示:

EEID START_EFF_DT END_EFF_DT
1111 2022/03/01 2022/03/01
1111 2022/03/04 2022/03/05
1111 2022/03/08 2022/03/10

我应该如何为需要创建的 crystal 报告设计此 SQL?

非常感谢任何帮助!

在 Oracle 12.1 及更高版本中,您可以使用 match_recognize 解决各种 pattern-matching 问题。

测试数据:

create table time_labor (eeid number, work_dt date, paycode varchar2(10));

alter session set nls_date_format = 'yyyy/mm/dd';

insert into time_labor (eeid, work_dt, paycode)
    select 1111, to_date('2022/03/01'), 'UNPAID'   from dual union all
    select 1111, to_date('2022/03/02'), 'REG_WORK' from dual union all
    select 1111, to_date('2022/03/03'), 'REG_WORK' from dual union all
    select 1111, to_date('2022/03/04'), 'UNPAID'   from dual union all
    select 1111, to_date('2022/03/05'), 'UNPAID'   from dual union all
    select 1111, to_date('2022/03/06'), 'SICK'     from dual union all
    select 1111, to_date('2022/03/07'), 'SICK'     from dual union all
    select 1111, to_date('2022/03/08'), 'UNPAID'   from dual union all
    select 1111, to_date('2022/03/09'), 'UNPAID'   from dual union all
    select 1111, to_date('2022/03/10'), 'UNPAID'   from dual
;

commit;

查询和输出:

select eeid, start_eff_dt, end_eff_dt
from   time_labor
match_recognize(
  partition by eeid
  order     by work_dt
  measures  first(work_dt) as start_eff_dt,
            last (work_dt) as   end_eff_dt
  pattern   ( unpaid+ )
  define    unpaid as paycode = 'UNPAID'
);

EEID  START_EFF_DT  END_EFF_DT
----  ------------  ------------
1111  2022/03/01    2022/03/01
1111  2022/03/04    2022/03/05
1111  2022/03/08    2022/03/10

match_recognize 机器对输入行进行分区,并根据 partition byorder by 子句在每个分区内对它们进行排序。它查找模式 unpaid+ 的匹配项,这意味着一个或多个连续的行被分类为 unpaid,其中该分类在 define 子句中定义。 (这里 unpaid 只是我为这个分类器选择的名称;这似乎是一个合理的选择,但它可以称为任何其他有效标识符,例如 xabc_2902) . match_recognize returns 默认情况下每个匹配一行,使用 measures 子句中的额外计算表达式。

对于无法使用 match_recognize 子句的旧版本 Oracle 数据库(版本 12.1 之前)的用户,这里有一个仅使用分析函数的解决方案和一个非常好的识别间隙和孤岛的方法,已知作为“固定差异”或“tabibito-san”方法。诀窍是在辅助子查询中创建附加分组表达式,标记为 grp(为了便于阅读,我在 with 子句中开始)。

with
  prep as (
    select eeid, work_dt, paycode,
           row_number() over (partition by eeid          order by work_dt) 
         - row_number() over (partition by eeid, paycode order by work_dt) 
           as grp
    from   time_labor
  )
select eeid, min(work_dt) as start_eff_dt, max(work_dt) as end_eff_dt
from   prep
group  by eeid, paycode, grp
having paycode = 'UNPAID'
order  by eeid, start_eff_dt
;

示例数据(用于测试)和此查询的输出可以在另一个答案中找到,该答案使用 match_recognize(仅限 Oracle 版本 >= 12.1)