【问题标题】:Find first day per group where time spans midnight boundaries查找时间跨越午夜边界的每组的第一天
【发布时间】:2018-11-15 10:52:51
【问题描述】:

我有一张表格,可以计算一个人的工作时间。我们有一个夜间团队,可以在下午 4 点之后随时登录,并在第二天早上 8 点之前注销。该表如下所示。

 Workdate      WorkHour
2018-11-13         20       -- this was the hour they logged on
2018-11-13         21
2018-11-13         22
2018-11-13         23
2018-11-14         0
2018-11-14         1
2018-11-14         2
2018-11-14         3
2018-11-14         4
2018-11-14         5        -- this was the hour they logged off

出于报告的目的,我们只想将这些工作时间与他们首次登录的日期相关联,在本例中为 2018 年 11 月 13 日。我的理想输出如下所示。

Workdate      WorkHour    ReportingDate
2018-11-13         20      2018-11-13 
2018-11-13         21      2018-11-13 
2018-11-13         22      2018-11-13 
2018-11-13         23      2018-11-13 
2018-11-14         0       2018-11-13 
2018-11-14         1       2018-11-13 
2018-11-14         2       2018-11-13 
2018-11-14         3       2018-11-13 
2018-11-14         4       2018-11-13 
2018-11-14         5       2018-11-13 

关于如何做到这一点的任何想法?感谢任何帮助

杰斯

【问题讨论】:

  • 您将需要使用窗口函数。您是否有唯一标识其中一名员工的字段?像 UserId 一样?
  • 是的,我们有一个用户 ID 和一个用户名字段
  • 我们可以假设一个人的停留时间永远不会超过 23 小时吗?
  • 这是正确的

标签: sql sql-server datetime sql-server-2012 gaps-and-islands


【解决方案1】:

您可以将其视为一个间隙和孤岛问题,其中连续的时间代表一个孤岛。您需要找到所有岛屿并找到每个岛屿的最短日期:

DECLARE @T TABLE (userid INT, workdate DATE, workhour INT);
INSERT INTO @t VALUES
(1, '2018-11-13', 20),
(1, '2018-11-13', 21),
(1, '2018-11-13', 22),
(1, '2018-11-13', 23),
(1, '2018-11-14',  0),
(1, '2018-11-14',  1),
(1, '2018-11-14',  2),
(1, '2018-11-14',  3),
(1, '2018-11-14',  4),
(1, '2018-11-14',  5),
(1, '2018-11-20', 6);

WITH cte1 AS (
    SELECT userid, workdate, workhour
         , DATEADD(HOUR, workhour, CAST(workdate AS DATETIME)) AS workdatetime
    FROM @t
), cte2 AS (
    SELECT userid, workdate, workhour
         , CASE WHEN DATEDIFF(HOUR, LAG(workdatetime) OVER (PARTITION BY userid ORDER BY workdate, workhour), workdatetime) = 1 THEN 0 ELSE 1 END AS chg
    FROM cte1
), cte3 AS (
    SELECT userid, workdate, workhour
         , SUM(chg) OVER (PARTITION BY userid ORDER BY workdate, workhour) AS grp
    FROM cte2
)
SELECT userid, workdate, workhour, MIN(workdate) OVER (PARTITION BY userid, grp) AS ReportingDate
FROM cte3
ORDER BY userid, workdate, workhour

【讨论】:

    【解决方案2】:

    当我有一个工作示例时,我会更新,但请尝试:

    SELECT
    MIN(WorkDate) OVER (PARTITION BY UserId ORDER BY WorkHour) [ReportingDate]
    FROM <YourTable>
    WHERE WorkDate >= CAST(DATEADD(DAY, -1, GETDATE()) AS DATE)
    

    【讨论】:

    • 这将是一份历史报告,因此有时可能会运行过去一段时间,即上周。
    • 是否有班次 ID 或可识别班次的内容?
    【解决方案3】:

    这是“gaps-and-islands”的变体。您可以通过减去枚举序列来识别相邻的时间。之后,您只需要对组进行最大值即可。

    select t.*,
           min(workdate) over (partition by datediff(hour, - seqnum, workdatehour) as imputed_workdate
    from (select t.*,
                 dateadd(hour, workhour, workdate) as workdatehour,
                 row_number() over (order by workdate, workhour) as seqnum
          from t
         ) t
    

    【讨论】:

    • datediff(day, 必须是 dateadd(hour,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-05
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 2018-02-04
    • 1970-01-01
    相关资源
    最近更新 更多