获取比较当前行和以前行的最小生效日期
Posted
技术标签:
【中文标题】获取比较当前行和以前行的最小生效日期【英文标题】:Fetch minimum effective date comparing current and previous rows 【发布时间】:2021-05-17 19:07:00 【问题描述】:我正在使用 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 并尝试比较当前和以前但无法确定在没有变化的时间范围内的最小值。
【问题讨论】:
Why should I tag my DBMS 也很高兴知道:您的 Oracle 数据库 版本 - 因为不同的版本可用不同的解决方案。如果您不知道您的版本,请运行select * from v$version
。
好的,我看到您已编辑以添加您的版本。更有用的是完整版 - 例如 12.1.0.2 或 12.2.0.1。 12.1 和 12.2(或 9.1. 和 9.2 等)之间存在显着差异 - 有时甚至在版本号的第四部分也存在有意义的差异。
【参考方案1】:
使用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'
;
【讨论】:
嗨 Mathguy,我的版本确实支持它,所以它工作得很好,如果你不介意我想知道如果没有“MATCH_RECOGNIZE”我们将如何做到这一点,是否有很多常见的 SQL 语法可以工作大多数数据库。 @okkadu - 添加了一个分析函数解决方案,其编写方式几乎与match_recognize
解决方案相同(甚至包括define
子句中的A
和B
分类器) match_recognize
和 where
子句镜像 pattern
子句中的 - -
语法以从输出中排除 B
行)。 Classifier 是 define
子句中的别名,如果您要在 match_recognize
子句中使用它们。
非常感谢,尤其是滞后的,我写过类似的查询,但我的头脑很生疏,我从来没有想过在外部查询上过滤。【参考方案2】:
实现这个查询的最简单也是我认为最快的方法是使用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;
【讨论】:
为什么你认为这比match_recognize
快?您是否曾经自己进行过测试,或者您是否在任何地方看到过这种说法?我所看到的一切都表明match_recognize
更快,所以听听你为什么“这么想”会很有趣。
另外,match_recognize
包含在 SQL 标准 - 2016 中,并被多个数据库产品支持(尽管支持分析函数的数量较少)。
参见例如standards.iso.org/ittf/PubliclyAvailableStandards/…(2016 SQL 标准中的match_recognize
)。以上是关于获取比较当前行和以前行的最小生效日期的主要内容,如果未能解决你的问题,请参考以下文章