【问题标题】:To fetch the rehire period using lag n lead function使用 lag n lead 函数获取重新雇佣期
【发布时间】:2023-01-03 05:30:02
【问题描述】:

想要检查该员工是否被重新雇用到合同中。如果他被重新雇用,则返还重新雇用期。如果多名员工被重新雇用,则返回他们所有的重新雇用期。

Sample data:(Table 'Contract')
Employee_id        Period         Contract 
111                  202204         1NA
111                  202205          1NA
111                  202206           1NA
112                  202207           1NA
112                  202208           1NA
111                  202209           1NA

在上述情况下,输出应该是,

Employee_id       Period           Contract
111               202209            1NA

查询应首先检查员工是否被重新雇用,如果是则返回重新雇用期。 如果该合同没有重新雇用,则返回 NULL。

除了 lag n lead 之外的任何其他逻辑也将受到赞赏!

提前致谢:)

Image of the sample data

【问题讨论】:

  • 你试过什么了 ?你有没有得到任何错误?另外请不要发布图片,但请发布格式正确的 ddl 和 dml 脚本。
  • 选择 t1.ARCHIVE_PERIOD、t1.PROJECT_OWNER、t1.CONTRACT_NUMBER 来自(SELECT ROW_NUMBER()超过(按 ARCHIVE_PERIOD 排序)ID、ARCHIVE_PERIOD、PROJECT_OWNER、来自 ARC_WORK_CONTRACT_GENERAL 的 CONTRACT_NUMBER WHERE CONTRACT_NUMBER='1N8501_t48' --AND1 项目所有者) JOIN (SELECT ROW_NUMBER() OVER (ORDER BY ARCHIVE_PERIOD) ID, ARCHIVE_PERIOD, PROJECT_OWNER, CONTRACT_NUMBER FROM ARC_WORK_CONTRACT_GENERAL WHERE CONTRACT_NUMBER='1N850248' --AND project_owner ='1110940') t1 ON t1.id = t0.id + 1 AND t0。 PROJECT_OWNER != t1.PROJECT_OWNER
  • 我试图比较连续的行,但是这个查询没有检查员工是否被重新雇用
  • 您究竟如何确定重新雇用?要将某一行标识为重新雇用行,必须满足什么条件?表的唯一键是什么?时期?期间+ employee_id?期限+合同? period + employee_id + contract?
  • @ThorstenKettner 重新雇用是在员工在一段时间后重新雇用合同时确定的在上面的数据中,员工 111 在员工 112 之后于 202209 被重新雇用。

标签: sql oracle


【解决方案1】:

使用LAG来判断是否为上一周期区间,则只选取区间>1的那些

create table  contracts (employee_id,period,contract) as 
(
  SELECT 111, 202204,'1NA' FROM DUAL UNION ALL
  SELECT 111, 202205,'1NA' FROM DUAL UNION ALL
  SELECT 111, 202206,'1NA' FROM DUAL UNION ALL
  SELECT 111, 202209,'1NA' FROM DUAL UNION ALL
  SELECT 112, 202207,'1NA' FROM DUAL UNION ALL
  SELECT 112, 202208,'1NA' FROM DUAL 
);

Table CONTRACTS created.

with contracts_w_lags (
   employee_id
   ,period
   ,last_period
   ,contract
) as ( select employee_id
             ,period
             ,lag(period)
               over(partition by employee_id
                    order by period)
             ,contract
  from contracts
)
select employee_id
      ,period 
      ,contract
  from contracts_w_lags
 where period - nvl( last_period ,period ) > 1;


EMPLOYEE_ID     PERIOD CON
----------- ---------- ---
        111     202209 1NA

请注意,您的样本数据仅在同一年内有期间。如果期间跨年,此示例将失败。

为了克服这个问题,创建一个带有行号的伪“周期”表来标识连续的行:

create table  contracts (employee_id,period,contract) as 
(
  SELECT 111, 202111,'1NA' FROM DUAL UNION ALL
  SELECT 111, 202112,'1NA' FROM DUAL UNION ALL
  SELECT 111, 202201,'1NA' FROM DUAL UNION ALL
  SELECT 111, 202203,'1NA' FROM DUAL UNION ALL
  SELECT 112, 202207,'1NA' FROM DUAL UNION ALL
  SELECT 112, 202208,'1NA' FROM DUAL 
);


Table CONTRACTS created.


with month_count ( cnt ) as 
( select months_between(
   to_date( max(period) ,'YYYYMM' )
  ,to_date( min(period) ,'YYYYMM' ))
  from contracts
),contract_start ( dt ) as 
( select to_date( min(period) ,'YYYYMM' )
  from contracts
),contract_periods ( period ,rn ) as 
( select to_char( add_months( c.dt ,level - 1 ) ,'YYYYMM' )
        ,row_number() over( order by add_months( c.dt ,level - 1 ) ) 
    from contract_start c
        ,month_count m 
    connect by level <= m.cnt + 1 
),contracts_w_lags ( employee_id ,period ,contract ,period_rn ,last_period_rn ) as 
( select c.employee_id
        ,c.period
        ,c.contract
        ,p.rn
        ,lag(p.rn) over(partition by c.employee_id order by p.rn )
  from contracts c
       join contract_periods p on c.period = p.period
)
select employee_id
      ,period
      ,contract
  from contracts_w_lags
 where period_rn - nvl( last_period_rn ,period_rn ) > 1;



EMPLOYEE_ID     PERIOD CON
----------- ---------- ---
        111     202209 1NA

【讨论】:

  • 如果不是在同一年内重新雇用,如何修改上述查询?
  • 检查第二个查询。注意...请提供体面的样本数据(涵盖所有情况)和正确的格式(创建表,插入语句)。如果您有其他数据,请更新您的问题,不要将其放入 cmets
  • 好的谢谢 !
【解决方案2】:

答案(在 cmets 之后)在最后....

目前尚不清楚您使用此示例数据的预期结果是什么:

WITH
    contracts (EMP_ID, PERIOD, CONTRACT) as 
        (
            SELECT 111, 202204, '1NA' FROM DUAL UNION ALL
            SELECT 111, 202205, '1NA' FROM DUAL UNION ALL
            SELECT 111, 202206, '1NA' FROM DUAL UNION ALL
            SELECT 112, 202207, '1NA' FROM DUAL UNION ALL
            SELECT 112, 202208, '1NA' FROM DUAL UNION ALL
            SELECT 111, 202209, '1NA' FROM DUAL 
        )

两个样本员工都有一些连续的多个时期。其中一个选项是显示具有多个周期的 emps 的第一个和最后一个周期:

SELECT  EMP_ID, Min(PREV_PERIOD) "FIRST_PERIOD", Max(PERIOD) "LAST_PERIOD", CONTRACT
FROM    (Select EMP_ID, PERIOD, CONTRACT, 
              LAG(PERIOD, 1, 0) OVER(Partition By EMP_ID Order By PERIOD) "PREV_PERIOD"
       From contracts)
WHERE   PREV_PERIOD != 0
GROUP BY  EMP_ID, CONTRACT
--  
--  R e s u l t :
--      EMP_ID FIRST_PERIOD LAST_PERIOD CONTRACT
--  ---------- ------------ ----------- --------
--         111       202204      202209 1NA      
--         112       202207      202208 1NA  

...另一个可能是向他们展示:

SELECT  EMP_ID, PERIOD "PERIOD", PREV_PERIOD "PREV_PERIOD", CONTRACT
FROM    (Select EMP_ID, PERIOD, CONTRACT, 
              LAG(PERIOD, 1, 0) OVER(Partition By EMP_ID Order By PERIOD) "PREV_PERIOD"
       From contracts)
WHERE   PREV_PERIOD != 0
--  
--  R e s u l t :
--      EMP_ID     PERIOD PREV_PERIOD CONTRACT
--  ---------- ---------- ----------- --------
--         111     202205      202204 1NA      
--         111     202206      202205 1NA      
--         111     202209      202206 1NA      
--         112     202208      202207 1NA  

...如果你想要同样的 LEAD() 函数

SELECT  EMP_ID, PERIOD "PERIOD", NEXT_PERIOD "NEXT_PERIOD", CONTRACT
FROM    (Select EMP_ID, PERIOD, CONTRACT, 
              LEAD(PERIOD, 1, 0) OVER(Partition By EMP_ID Order By PERIOD) "NEXT_PERIOD"
       From contracts)
WHERE   NEXT_PERIOD != 0
--  
--  R e s u l t :
--      EMP_ID     PERIOD NEXT_PERIOD CONTRACT
--  ---------- ---------- ----------- --------
--         111     202204      202205 1NA      
--         111     202205      202206 1NA      
--         111     202206      202309 1NA      
--         112     202207      202208 1NA     
--         112     202208      202207 1NA      

它几乎是一样的 - 只是显示下一个时期而不是上一个时期。
笔记:如果重新雇用一份合同意味着同一份合同,那么 -
OVER(按 EMP_ID 分区,合同 ....)

做相反的事情(非连续期):

SELECT  EMP_ID, PERIOD "PERIOD", NEXT_PERIOD "NEXT_PERIOD", CONTRACT
FROM    (Select EMP_ID, PERIOD, CONTRACT, 
              LEAD(PERIOD, 1, 0) OVER(Partition By EMP_ID Order By PERIOD) "NEXT_PERIOD"
       From contracts)
WHERE   NEXT_PERIOD != 0 And CASE WHEN SubStr(NEXT_PERIOD, 1, 4) = SubStr(PERIOD, 1, 4) 
                                THEN NEXT_PERIOD - PERIOD
                           ELSE NEXT_PERIOD - (PERIOD + 88)  -- handling the year  change
                           END > 1
--  
--  R e s u l t :
--      EMP_ID     PERIOD NEXT_PERIOD CONTRACT
--  ---------- ---------- ----------- --------
--         111     202206      202209 1NA        

【讨论】:

  • 我认为“重新雇用”OP 意味着没有以前的时期(而且这不是该员工的第一个时期)但是有更早的记录。在示例数据中,员工 ID 111 是重新雇用的,因为他在 202209 期间有合同,但在 202208 期间没有合同,并且他有 202206 及之前的记录。
  • @KoenLostrie 如前所述 - 没有预期结果还不清楚......
【解决方案3】:

从 Oracle 12 开始,您可以使用 MATCH_RECOGNIZE 执行逐行模式匹配:

SELECT  contract, emp_id
FROM    contracts
MATCH_RECOGNIZE(
  PARTITION BY contract
  ORDER BY period
  MEASURES
    FIRST(emp_id) AS emp_id
  AFTER MATCH SKIP TO FIRST different_emp
  PATTERN (emp+ different_emp+ emp+)
  DEFINE
    emp           AS FIRST(emp_id) =  emp_id,
    different_emp AS FIRST(emp_id) != emp_id
);

其中,对于示例数据:

CREATE TABLE contracts (EMP_ID, PERIOD, CONTRACT) as 
SELECT 111, 202204, '1NA' FROM DUAL UNION ALL
SELECT 111, 202205, '1NA' FROM DUAL UNION ALL
SELECT 111, 202206, '1NA' FROM DUAL UNION ALL
SELECT 112, 202207, '1NA' FROM DUAL UNION ALL
SELECT 112, 202208, '1NA' FROM DUAL UNION ALL
SELECT 111, 202209, '1NA' FROM DUAL;

输出:

CONTRACT EMP_ID
1NA 111

fiddle

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-19
    相关资源
    最近更新 更多