查找时间跨越午夜边界的每组的第一天

Posted

技术标签:

【中文标题】查找时间跨越午夜边界的每组的第一天【英文标题】: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 小时吗? 这是正确的 【参考方案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,

以上是关于查找时间跨越午夜边界的每组的第一天的主要内容,如果未能解决你的问题,请参考以下文章

PHP获取一个月的第一天和最后一天

在javascript中查找上个月的第一天

在javascript中查找上个月的第一天

Qdate,获取月份的第一天

noip2012

在 SQL Server Reporting Services (VB.Net) 中查找上一个日历月的第一天和最后一天