【问题标题】:SQL to Find Number of Weeks an Employee Was Active Between Two DatesSQL 查找员工在两个日期之间活跃的周数
【发布时间】:2013-05-03 00:47:38
【问题描述】:

我有一个表格,其中列出了员工活跃/不活跃的日期,我想计算员工在特定日期范围内活跃的周数。

所以表 (ps_job) 将具有如下值:

EMPLID     EFFDT       HR_STATUS
------     -----       ------
1000       01-Jul-11   A
1000       01-Sep-11   I
1000       01-Jan-12   A
1000       01-Mar-12   I
1000       01-Sep-12   A

查询需要向我显示此 empid 从 2011 年 7 月 1 日到 2012 年 12 月 31 日活跃的周数。

所需的结果集是:

EMPLID     WEEKS_ACTIVE
------     ------------
1000       35

我通过添加以下 SQL 的结果得到数字 35:

SELECT (NEXT_DAY('01-Sep-11','SUNDAY') - NEXT_DAY('01-Jul-11','SUNDAY'))/7 WEEKS_ACTIVE FROM DUAL;
SELECT (NEXT_DAY('01-Mar-12','SUNDAY') - NEXT_DAY('01-Jan-12','SUNDAY'))/7 WEEKS_ACTIVE FROM DUAL;
SELECT (NEXT_DAY('31-Dec-12','SUNDAY') - NEXT_DAY('01-Sep-12','SUNDAY'))/7 WEEKS_ACTIVE FROM DUAL;

问题是我似乎无法弄清楚如何创建一个查询语句,该语句将遍历特定日期范围内每个员工的所有行,并只返回每个 empid 以及它们处于活动状态的周数。我更喜欢使用基本 SQL 而不是 PL/SQL,以便我可以将其传输到可由用户运行的 PeopleSoft 查询,但如果需要,我愿意使用 Oracle SQL Developer 为用户运行它。

数据库:Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

【问题讨论】:

  • 您能更好地解释您的需求吗?您的表中可以有多个条目,对吗?我的意思是,您每周都会存储员工是否活跃?还是每个月??
  • 因此,每当雇用或终止员工时,都会更新该表。在这种情况下,员工于 2011 年 7 月 1 日被聘用,因此他们的 HR_STATUS 为 A(ctive)。然后他们在 2011 年 9 月 1 日被终止,所以他们的 HR_STATUS 是 I(nactive)。然后他们在 2012 年 1 月 1 日被重新雇用,2012 年 3 月 1 日再次被终止,然后在 2012 年 9 月 1 日重新雇用,直到今天仍然活跃。这是一个极端的例子,但显示了员工在活动和非活动之间来回切换如何使查询变得更加困难。
  • 在您的示例中,您试图将日期推到星期日。假设一名员工在本周的星期五(2013 年 5 月 10 日)被雇用并在下周的星期一(2013 年 5 月 13 日)终止。他们的“周数”可以是 0(3 天四舍五入 = 0 周)、1(3 天 = 一周的一部分)或 2(在两个不同的周内活动)。您要举报哪个?
  • 这是个好问题。我不知道客户会如何回答这个问题,所以我会给出我最好的猜测。在这种情况下,我会说 1。他们现在只想要一个粗略的估计。我希望我能说得更明确一点。

标签: sql oracle oracle11g peoplesoft


【解决方案1】:

如果客户只是想要一个粗略的估计,我会从每次工作的天数开始,除以 7 并四舍五入。

诀窍是将活动日期与其对应的非活动日期对齐,我能想到的最好方法是分别挑选活动日期和非活动日期,按日期排列它们,然后将它们重新组合在一起EmplID 和排名。 ROW_NUMBER() 分析函数是在这种情况下排名的最佳方式:

WITH
  EmpActive AS (
    SELECT
        EmplID,
        EffDt,
        ROW_NUMBER() OVER (PARTITION BY EmplID ORDER BY EffDt NULLS LAST) DtRank
      FROM ps_job
      WHERE HR_Status = 'A'
  ),
  EmpInactive AS (
   SELECT
      EmplID,
      EffDt,
      ROW_NUMBER() OVER (PARTITION BY EmplID ORDER BY EffDt NULLS LAST) DtRank
    FROM ps_job
    WHERE HR_Status = 'I'
  )
SELECT
  EmpActive.EmplID,
  EmpActive.EffDt AS ActiveDate,
  EmpInactive.EffDt AS InactiveDate,
  ROUND((NVL(EmpInactive.EffDt, TRUNC(SYSDATE)) - EmpActive.EffDt) / 7) AS WeeksActive
FROM EmpActive
LEFT JOIN EmpInactive ON
    EmpActive.EmplID = EmpInactive.EmplID AND
    EmpActive.DtRank = EmpInactive.DtRank

EmplID = 1000 的第三个 gig 有一个活动日期,但没有非活动日期,因此 NULLS LAST 在ROW_NUMBER 排序和两个子查询之间的左连接。

我在这里使用了“天 / 7”数学;当您收到客户的回复时,您可以替换您需要的东西。请注意,如果没有相应的非活动日期,则查询使用当前日期。

here 有一个 SQLFiddle。

【讨论】:

    【解决方案2】:

    以下内容应该适用于您正在尝试做的事情。我确实必须在 NVL 语句中硬编码结束日期

    SELECT emplid,
           hr_status,
           ROUND(SUM(end_date - start_date)/7) num_weeks
      FROM (SELECT emplid,
                   hr_status,
                   effdt start_date,
                   NVL(LEAD(effdt) OVER (PARTITION BY emplid ORDER BY effdt), 
                                            TO_DATE('12312012','MMDDYYYY')) end_date
              FROM ps_job
           )
     WHERE hr_status = 'A'
     GROUP BY emplid,
              hr_status
     ORDER BY emplid
    

    内部查询将从表中拉取员工和HR状态信息,并使用effdt列作为开始日期,并使用LEAD解析函数从表中获取下一个effdt日期值,表示下一个开始日期status 等将是当前行的 end_date。如果 LEAD 函数返回 NULL,我们会为其分配您想要的完成日期(2012 年 12 月 31 日)。然后他的 out 语句将结果集限制为具有活动 HR 状态的记录并计算周数。

    【讨论】:

      【解决方案3】:

      这里我在子查询中使用lead 来获取下一个日期,然后在外部查询中对间隔求和:

      with q as (
          select EMPLID, EFFDT, HR_STATUS
              , lead (EFFDT, 1) over (partition by EMPLID order by EFFDT) as NEXT_EFFDT
          from ps_job
          order by EMPLID, EFFDT
      )
      select EMPLID
          , trunc(sum((trunc(coalesce(NEXT_EFFDT, current_timestamp)) - trunc(EFFDT)) / 7)) as WEEKS_ACTIVE
      from q
      where HR_STATUS = 'A'
      group by EMPLID;
      

      如果coalesce 函数找不到匹配的I 记录(员工是当前员工),它将获取系统日期。如果这是您的规范,您可以替换年底。

      请注意,我没有进行任何严格的测试来查看您的条目是否按 A/I/A/I 等排序,因此如果您知道您的数据需要,您可能需要添加这种性质的检查。

      请随时在SQL Fiddle 玩这个。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-07-25
        • 2020-04-03
        • 1970-01-01
        • 1970-01-01
        • 2021-05-21
        • 2018-06-17
        • 1970-01-01
        相关资源
        最近更新 更多