【问题标题】:Determining consecutive and independent PTO days确定连续和独立的 PTO 天数
【发布时间】:2019-02-16 13:29:48
【问题描述】:

根据反馈,我正在调整我的问题。

我在 Presto 数据库上使用 SQL。

我的目标是报告自 2018 年初以来连续几天使用 PTO 或病假的员工。我想要的输出将包含员工在开始和结束日期所花费的各个时间岛,大致如下:

我使用的主表是 d_employee_time_off

只有两个 time_off_type_name:PTO 和 Sick Leave。

ds 是一个日期戳,我使用最新的 ds(通常是当前日期)

我可以访问名为 d_date 的日期表

我可以在 d_employee_time_off.time_off_date = d_date.full_date 加入表格

我希望我以一种可以理解的方式来组织这个问题。

【问题讨论】:

  • @KenWhite 这是一个“孤岛”要求,常规 min() max() 和 group by 无法满足。
  • 请您仅提供源数据作为示例,而不是向我们显示一些查询输出(这是不正确的)。查看您尝试过的查询也很有用。
  • @Used_By_Already 源数据实际上只是employee_id、类型(只是PTO 或病假)、日期和time_off_in_days(始终为1)。我加入了日期表以获取日期名称,并使用超前和滞后函数来获取上一个和未来的日期。您将此问题确定为“孤岛”要求,这使我阅读了一些很好的内容,因此我会在尝试找到解决方案时对此进行重点讨论并进行更新
  • previous question。它写得非常好,因为它包括一个自包含的表结构 (DDL)、用于填充这些表的数据,以及查询尝试根据表检索数据以及预期的输出。换句话说,一个完整的minimal reproducible example提供这些相同的工件在你的问题中将大大有助于其他人能够帮助你解决你的问题。您也可以将rextester.com 用于您的 DDL 和数据。
  • 谢谢@Igor。我会看看那个问题和你提供的其他链接。

标签: sql presto


【解决方案1】:

我认为这里需要将休息日材料加入日历表。

在下面的示例解决方案中,我正在“即时”生成此解决方案,但我认为您确实有自己的解决方案。同样在我的示例中,我使用了字符串“Monday”并从该字符串向后移动(或者,您可以使用“Friday”并向前移动)。我不热衷于依赖语言的解决方案,但由于我不是 Presto 用户,因此无法在 Presto 上测试任何东西。因此,下面的示例使用了您自己的一些逻辑,但使用了我相信您可以转换为 Presto 的 SQL Server 语法:

查询:

;WITH
Digits AS (
          SELECT 0 AS digit UNION ALL
          SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL  
          SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL 
          SELECT 9
          )
, cal AS (
          SELECT 
                 ca.number
               , dateadd(day,ca.number,'20180101') as cal_date
               , datename(weekday,dateadd(day,ca.number,'20180101')) weekday
          FROM Digits [1s]
          CROSS JOIN Digits [10s]
          CROSS JOIN Digits [100s] /* add more like this as needed */
          cross apply (
              SELECT 
                      [1s].digit 
                    + [10s].digit * 10
                    + [100s].digit * 100  /* add more like this as needed */
                    AS number
              ) ca
          )
, time_off AS (
        select
            *
        from cal
        inner join mytable t on (cal.cal_date = t.time_off_date and cal.weekday <> 'Monday') 
                             or (cal.cal_date between dateadd(day,-2,t.time_off_date) 
                                  and t.time_off_date and datename(weekday,t.time_off_date) = 'Monday')
        )
, starting_points AS (
        SELECT
            employee_id,
            cal_date,
            dense_rank() OVER(partition by employee_id
                ORDER BY
                    time_off_date
            ) AS rownum
        FROM
            time_off A
        WHERE
            NOT EXISTS (
                SELECT
                    *
                FROM
                    time_off B
                WHERE
                    B.employee_id = A.employee_id
                    AND B.cal_date = DATEADD(day, -1, A.cal_date)
            )
    )
, ending_points AS (
        SELECT
            employee_id,
            cal_date,
            dense_rank() OVER(partition by employee_id
                ORDER BY
                    time_off_date
            ) AS rownum
        FROM
            time_off A
        WHERE
            NOT EXISTS (
                SELECT
                    *
                FROM
                    time_off B
                WHERE
                    B.employee_id = A.employee_id
                    AND B.cal_date = DATEADD(day, 1, A.cal_date)
            )
    )
SELECT
    S.employee_id,
    S.cal_date AS start_range,
    E.cal_date AS end_range
FROM
    starting_points S
JOIN
    ending_points E
    ON E.employee_id = S.employee_id
    AND E.rownum = S.rownum
order by employee_id
    , start_range

结果:

    employee_id start_range end_range
1   200035      02.01.2018  02.01.2018 
2   200035      20.04.2018  27.04.2018 
3   200037      27.01.2018  29.01.2018 
4   200037      31.03.2018  02.04.2018 

见:http://rextester.com/MISZ50793

CREATE TABLE mytable(
   ID INT NOT NULL
  ,employee_id      INTEGER  NOT NULL
  ,type             VARCHAR(3) NOT NULL
  ,time_off_date         DATE  NOT NULL
  ,time_off_in_days INT NOT NULL
);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (1,200035,'PTO','2018-01-02',1);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (2,200035,'PTO','2018-04-20',1);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (3,200035,'PTO','2018-04-23',1);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (4,200035,'PTO','2018-04-24',1);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (5,200035,'PTO','2018-04-25',1);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (6,200035,'PTO','2018-04-26',1);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (7,200035,'PTO','2018-04-27',1);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (8,200037,'PTO','2018-01-29',1);
INSERT INTO mytable(id,employee_id,type,time_off_date,time_off_in_days) VALUES (9,200037,'PTO','2018-04-02',1);

【讨论】:

  • 希望您现在可以看到提供示例数据(而不是查询结果)的必要性,以及让您的某些日历表可供使用的用处。提供样本可以更快地回复您的问题。
  • 非常感谢。我是一个菜鸟,还在学习,所以请原谅我糟糕的问题结构(我相信我正在重组我的问题,因为你正在提供答案)。非常感谢您的帮助,我保证会努力变得更好。
  • 一点也不,你的问题远非糟糕,只是指出可能的改进——这也最终对你有利。但是不要使用数据图像,因为它需要有人转录,所以使用起来不是很友好。简单的文本表格要好得多。如果我的答案是查询的图像,则图像 :)。不知道为什么你也删除了你的查询,这应该留在问题中。
  • 谢谢。我将重新编辑问题,因为我希望能够提出好的问题。再次感谢您,非常感谢您的帮助。
  • :) 好的,但我会保留您阅读这些stackoverflow.com/help/mcve 和/或meta.stackoverflow.com/questions/333952/… 的努力,然后将其付诸实践以解决您的下一个问题。
猜你喜欢
  • 2016-05-31
  • 1970-01-01
  • 2022-07-19
  • 2010-11-13
  • 1970-01-01
  • 1970-01-01
  • 2013-08-23
  • 2017-06-02
  • 1970-01-01
相关资源
最近更新 更多