获取比较当前行和上一行的最小生效日期
Fetch minimum effective date comparing current and previous rows
我在 Oracle 12c 上工作,我需要根据列的分组获取最小生效日期,但是只有一段时间,直到没有变化,示例如下:
假设我们的员工 ID 在一段时间内更改了部门,并且我们想要捕获每个部门的最低生效日期,但最低生效日期仅在更改之前。
EMP_ID
EFF_DT
DEPT_ID
100
01/01/2015
ENGINEERING
100
01/01/2016
ENGINEERING
100
01/01/2017
ENGINEERING
100
01/01/2018
FINANCE
100
01/01/2019
FINANCE
100
01/01/2020
ENGINEERING
100
01/01/2021
ENGINEERING
目标:
EMP_ID
EFF_DT
DEPT_ID
100
01/01/2015
ENGINEERING
100
01/01/2018
FINANCE
100
01/01/2020
ENGINEERING
如何实现?我尝试做 LAG 并尝试比较当前和以前的但无法在没有变化的时间范围内识别最小值。
使用 match_recognize
的解决方案(自数据库版本 12.1 起可用)
正在设置测试数据:
alter session set nls_date_format='mm/dd/yyyy';
create table my_table (emp_id, eff_dt, dept_id) as
select 100, to_date('01/01/2015'), 'ENGINEERING' from dual union all
select 100, to_date('01/01/2016'), 'ENGINEERING' from dual union all
select 100, to_date('01/01/2017'), 'ENGINEERING' from dual union all
select 100, to_date('01/01/2018'), 'FINANCE' from dual union all
select 100, to_date('01/01/2019'), 'FINANCE' from dual union all
select 100, to_date('01/01/2020'), 'ENGINEERING' from dual union all
select 100, to_date('01/01/2021'), 'ENGINEERING' from dual
;
查询和输出:
select emp_id, eff_dt, dept_id
from my_table
match_recognize(
partition by emp_id
order by eff_dt
all rows per match
pattern ( a {- b* -} )
define b as dept_id = a.dept_id
);
EMP_ID EFF_DT DEPT_ID
---------- ---------- -----------
100 01/01/2015 ENGINEERING
100 01/01/2018 FINANCE
100 01/01/2020 ENGINEERING
简要说明:
match_recognize
子句按 emp_id
对输入行进行分区,并按 eff_dt
对它们进行排序。然后,它在每个分区内并按照日期顺序进一步将行划分为“匹配项”,匹配 pattern
子句中给出的模式。这是单个 a
行后跟 0 个或多个 b
行,其中 b
被定义(在 define
子句中)以要求 dept_id
是与比赛的第一行相同。一行成为 a
行没有条件;任何行,如果它不能被分类为 b
,将被分类为 a
(并且它将开始一个新的匹配!)
返回匹配中的“所有”行,pattern
子句中包含在 {- -}
中的行除外。即:返回每个匹配项中的 a
行(第一行),而不返回 b
行。完全符合规定。
编辑
对于 Oracle 11.2 或更低版本,以及还不支持 match_recognize
的数据库产品(Oracle 除外),这可以通过解析函数来完成,绝大多数数据库都支持这些函数数据库。
以下版本与 match_recognize
解决方案几乎完全相同:
select emp_id, eff_dt, dept_id
from (
select emp_id, eff_dt, dept_id,
case when lag(dept_id) over (partition by emp_id
order by eff_dt) = dept_id
then 'B' else 'A' end as classifier
from my_table
)
where classifier = 'A'
;
实现此查询的最简单也是我认为最快的方法使用 lag()
:
select t.*
from (select t.*,
lag(dept_id) over (partition by emp_id order by eff_dt) as prev_dept_id
from t
) t
where prev_dept_id is null or prev_dept_id <> dept_id;
我在 Oracle 12c 上工作,我需要根据列的分组获取最小生效日期,但是只有一段时间,直到没有变化,示例如下:
假设我们的员工 ID 在一段时间内更改了部门,并且我们想要捕获每个部门的最低生效日期,但最低生效日期仅在更改之前。
EMP_ID | EFF_DT | DEPT_ID |
---|---|---|
100 | 01/01/2015 | ENGINEERING |
100 | 01/01/2016 | ENGINEERING |
100 | 01/01/2017 | ENGINEERING |
100 | 01/01/2018 | FINANCE |
100 | 01/01/2019 | FINANCE |
100 | 01/01/2020 | ENGINEERING |
100 | 01/01/2021 | ENGINEERING |
目标:
EMP_ID | EFF_DT | DEPT_ID |
---|---|---|
100 | 01/01/2015 | ENGINEERING |
100 | 01/01/2018 | FINANCE |
100 | 01/01/2020 | ENGINEERING |
如何实现?我尝试做 LAG 并尝试比较当前和以前的但无法在没有变化的时间范围内识别最小值。
使用 match_recognize
的解决方案(自数据库版本 12.1 起可用)
正在设置测试数据:
alter session set nls_date_format='mm/dd/yyyy';
create table my_table (emp_id, eff_dt, dept_id) as
select 100, to_date('01/01/2015'), 'ENGINEERING' from dual union all
select 100, to_date('01/01/2016'), 'ENGINEERING' from dual union all
select 100, to_date('01/01/2017'), 'ENGINEERING' from dual union all
select 100, to_date('01/01/2018'), 'FINANCE' from dual union all
select 100, to_date('01/01/2019'), 'FINANCE' from dual union all
select 100, to_date('01/01/2020'), 'ENGINEERING' from dual union all
select 100, to_date('01/01/2021'), 'ENGINEERING' from dual
;
查询和输出:
select emp_id, eff_dt, dept_id
from my_table
match_recognize(
partition by emp_id
order by eff_dt
all rows per match
pattern ( a {- b* -} )
define b as dept_id = a.dept_id
);
EMP_ID EFF_DT DEPT_ID
---------- ---------- -----------
100 01/01/2015 ENGINEERING
100 01/01/2018 FINANCE
100 01/01/2020 ENGINEERING
简要说明:
match_recognize
子句按 emp_id
对输入行进行分区,并按 eff_dt
对它们进行排序。然后,它在每个分区内并按照日期顺序进一步将行划分为“匹配项”,匹配 pattern
子句中给出的模式。这是单个 a
行后跟 0 个或多个 b
行,其中 b
被定义(在 define
子句中)以要求 dept_id
是与比赛的第一行相同。一行成为 a
行没有条件;任何行,如果它不能被分类为 b
,将被分类为 a
(并且它将开始一个新的匹配!)
返回匹配中的“所有”行,pattern
子句中包含在 {- -}
中的行除外。即:返回每个匹配项中的 a
行(第一行),而不返回 b
行。完全符合规定。
编辑
对于 Oracle 11.2 或更低版本,以及还不支持 match_recognize
的数据库产品(Oracle 除外),这可以通过解析函数来完成,绝大多数数据库都支持这些函数数据库。
以下版本与 match_recognize
解决方案几乎完全相同:
select emp_id, eff_dt, dept_id
from (
select emp_id, eff_dt, dept_id,
case when lag(dept_id) over (partition by emp_id
order by eff_dt) = dept_id
then 'B' else 'A' end as classifier
from my_table
)
where classifier = 'A'
;
实现此查询的最简单也是我认为最快的方法使用 lag()
:
select t.*
from (select t.*,
lag(dept_id) over (partition by emp_id order by eff_dt) as prev_dept_id
from t
) t
where prev_dept_id is null or prev_dept_id <> dept_id;