【问题标题】:How to get first change of column for a row in Oracle table through SQL?如何通过 SQL 获取 Oracle 表中一行的第一次更改列?
【发布时间】:2019-11-21 23:18:37
【问题描述】:

维度表类型 2 记录一行随时间的变化 (SCD)。我想根据另一列说日期找到列的第一个值更改(应该是常见模式)。

例如,带有 (emp_id, insert_date, status, address) 的员工表,其中状态表示工作、不工作、假期等(一些固定值)。比如说,这家奇怪的公司每天都在密切关注员工。现在我想知道员工在过去一年的最后一个假期是什么时候开始的。以下将不起作用

select distinct empid, max(insert_date) where status = 'vacation'
where insert_date > today - 1 year --- know not correct syntax but to simplify
group by empid 

因为它会选择状态为假期的最后一条记录,但可能有许多带有假期的记录,因为记录可能由于地址更改等其他原因而发生变化,例如:

emp_id insert_dt status     address
1      9/1/2019   working   123
1      9/2/2019   working   1234
1      9/3/2019   vacation  1234
1      9/4/2019   vacation  12345   --- address change
1      9/5/2019   working   12345

所以我想第一次发生从工作到假期的转换,即 9/3(不是 9/4),min 也不起作用。

我们无法更改表架构,PL/SQL 也无法使用嵌入在某些编程语言等中的 SQL,只能使用普通 SQL。如果您知道 Oracle 中的任何(分析)函数可以运行 lambda 或使用编码列等模拟它,那将是理想的。

谢谢 S

【问题讨论】:

    标签: sql database oracle data-warehouse


    【解决方案1】:

    使用lag():

    select t.*
    from (select t.*,
                 lag(status) over (partition by emp_id order by insert_dt) as prev_status
          from t
         ) t
    where status = 'vacation' and
          (prev_status <> status or prev_status is null);
    

    编辑:

    如果您想要员工的最近时间,您可以使用聚合:

    select emp_id, max(insert_dt)
    from (select t.*,
                 lag(status) over (partition by emp_id order by insert_dt) as prev_status
          from t
         ) t
    where status = 'vacation' and
          (prev_status <> status or prev_status is null)
    group by emp_id;
    

    您也可以在特定时间段内添加过滤。

    【讨论】:

    • 但这会给我所有有开关的记录。如果我想知道员工在过去一年中开始休假的最后时间/日期,我需要通过对 emp_id 进行分区并按 insert_dt desc 排序,将其包装在另一个 select use last_value() 中,不是吗?如果是这样,有没有更有效的方法(我不知道基数就看不到)?
    【解决方案2】:

    所以我想第一次发生从工作到度假的转变

    我将首先使用lag() 来获取每个emp_id 的先前状态,按insert_dt 排序。然后,确定状态从工作切换到休假的记录,并按insert_dt 对每个员工进行排名。最后,过滤每个员工的第一条记录:

    select e.*
    from (
        select 
            e.*,
            row_number() over(partition by emp_id order by insert_dt) rn
        from (
            select
                e.*,
                lag(status) over(partition by emp_id order by insert_dt) lag_status
            from employee e
            where insert_date > add_months(trunc(sysdate), -12)
        ) e
        where lag_status = 'working' and status = 'vacation'
    ) e
    where rn = 1
    

    【讨论】:

    • GMB,你不是和我在对 Gordon 的评论中所说的一样吗?要获得最后一次,我不必按 insert_dt 降序对 row_number 进行排序吗?
    • @Noname: 是的,这个查询为每个员工提供了一条记录,他们第一次从工作状态切换到休假。如果您想要最后一次,那么您确实可以将row_number() 更改为order by insert_dt desc
    猜你喜欢
    • 2011-07-21
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 2015-04-09
    • 1970-01-01
    相关资源
    最近更新 更多