获取比较当前行和以前行的最小生效日期

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 子句中的AB 分类器) match_recognizewhere 子句镜像 pattern 子句中的 - - 语法以从输出中排除 B 行)。 Classifierdefine 子句中的别名,如果您要在 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)。

以上是关于获取比较当前行和以前行的最小生效日期的主要内容,如果未能解决你的问题,请参考以下文章

比较2个日期是否相同

如何更新主表当前行的输入类型文本中的值从模态窗口表中获取值

JS获取当前日期比较日期大小

sql中如何获取当天时间的零点

R:获取矩阵的最小元素的行和列名,但最小值!= 0

java输入的日期与当前的日期做比较?