你曾经看过那张冰山海报,80% 都在水下,所以你不会考虑它。
你的问题很像。它看起来无伤大雅,但乍一看,却有一只隐藏的野兽。在这种情况下,那个野兽就是你必须这样做的数据结构,加上轮班似乎跨越几天的事实。短篇小说这里是你在 SQL 中的冰山解决方案。长话短说。
SELECT
base.[Employee-ID],
DATEPART(year, base.[Date]) AS [Year],
DATEPART(month, base.[Date]) AS [Month],
DATEPART(day, base.[Date]) AS [Day],
SUM(base.HoursWorked) AS TotalHoursAtWork,
SUM(base.HoursOnBreak) AS TotalBreakHours,
SUM(base.HoursWorked) - SUM(base.HoursOnBreak) AS TotalHoursWorked
FROM
(
SELECT
[Employee-ID],
CAST(StartDateTime AS DATE) [Date],
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
DATEDIFF(minute, StartDateTime, EndDateTime) / 60.0
ELSE
DATEDIFF(minute, StartDateTime, DATEADD(day, DATEDIFF(day, 0, StartDateTime), 1)) / 60.0
END HoursWorked,
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
BreakHours
ELSE
BreakHours / 2
END HoursOnBreak
FROM (
SELECT
[ID],
[Employee-ID],
[Date],
[Coming-time],
[Leaving-time],
[Break(in hours)] AS BreakHours,
CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
CASE
WHEN [Coming-time] <= [Leaving-time] THEN
CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
ELSE
CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
END AS EndDateTime
FROM
[sandbox].[dbo].[TblEmployee]
) AS firstDay
UNION ALL
SELECT
[Employee-ID],
CAST(EndDateTime AS DATE) [Date],
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
0
ELSE
DATEDIFF(minute, DATEADD(day, DATEDIFF(day, 0, EndDateTime), 0), EndDateTime) / 60.0
END HoursWorked,
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
0
ELSE
BreakHours / 2
END HoursOnBreak
FROM (
SELECT
[ID],
[Employee-ID],
[Date],
[Coming-time],
[Leaving-time],
[Break(in hours)] AS BreakHours,
CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
CASE
WHEN [Coming-time] <= [Leaving-time] THEN
CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
ELSE
CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
END AS EndDateTime
FROM
[sandbox].[dbo].[TblEmployee]
) AS secondDay
) AS base
GROUP BY
base.[Employee-ID],
DATEPART(year, base.[Date]),
DATEPART(month, base.[Date]),
DATEPART(day, base.[Date])
那么为什么会有这些东西呢?问题是日期和时间的时间报告存在根本缺陷。这实际上是在某个桶中工作的小时数,例如每天、每个任务、每个项目等。
由于您要查看每天/每月/每年的工作小时数,因此我们只需要获取这些数据并将其放入最低公分母中,即每天的小时数。
第 1 步 - 将传入和传出转换为日期时间格式。这让我们获得了相等的数据类型,顺便说一下,当即将到来的时间大于离开时会发生什么。
SELECT
[ID],
[Employee-ID],
[Date],
[Coming-time],
[Leaving-time],
[Break(in hours)],
CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
CASE
WHEN [Coming-time] <= [Leaving-time] THEN
CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
ELSE
CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
END AS EndDateTime
FROM
[sandbox].[dbo].[TblEmployee]
第 2 步 - 现在我们已经获得了日期时间的真实表示,将其转换为每天的小时数。问题是班次有时会跨越几天。 ruh roh raggy。
所以让我们把它分开,在多天的shitfs中让每天休息一半。
警告:假设:
- 如果离开时间是“之后”(小时:分钟超过),则假定离开时间是第二天的时间。
- shiftft 永远不会超过 24 小时。如果某人早上 8 点进来,早上 10 点离开,则假定是 2 小时,而不是 26 小时。如果您没有离开的日期,这就是生活。
- 休息并不总是一半一半,所以有些
那里的变化。
SELECT
[Employee-ID],
CAST(StartDateTime AS DATE) FirstDay,
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
DATEDIFF(minute, StartDateTime, EndDateTime) / 60.0
ELSE
DATEDIFF(minute, StartDateTime, DATEADD(day, DATEDIFF(day, 0, StartDateTime), 1)) / 60.0
END FirstDayHours,
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
BreakHours
ELSE
BreakHours / 2
END FirstDayBreak,
CAST(EndDateTime AS DATE) SecondDay,
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
0
ELSE
DATEDIFF(minute, DATEADD(day, DATEDIFF(day, 0, EndDateTime), 0), EndDateTime) / 60.0
END SecondDayHours,
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
0
ELSE
BreakHours / 2
END SecondDayBreak
FROM (
SELECT
[ID],
[Employee-ID],
[Date],
[Coming-time],
[Leaving-time],
[Break(in hours)] AS BreakHours,
CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
CASE
WHEN [Coming-time] <= [Leaving-time] THEN
CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
ELSE
CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
END AS EndDateTime
FROM
[sandbox].[dbo].[TblEmployee]
) AS base
第 3 步 - 问题是我们需要在一天的列中进行分组。下面的联合只是将第一天和第二天的逻辑分成两个单独的查询。高效 = 地狱不,功能 - 是的。
SELECT
[Employee-ID],
CAST(StartDateTime AS DATE) [Date],
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
DATEDIFF(minute, StartDateTime, EndDateTime) / 60.0
ELSE
DATEDIFF(minute, StartDateTime, DATEADD(day, DATEDIFF(day, 0, StartDateTime), 1)) / 60.0
END HoursWorked,
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
BreakHours
ELSE
BreakHours / 2
END HoursOnBreak
FROM (
SELECT
[ID],
[Employee-ID],
[Date],
[Coming-time],
[Leaving-time],
[Break(in hours)] AS BreakHours,
CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
CASE
WHEN [Coming-time] <= [Leaving-time] THEN
CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
ELSE
CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
END AS EndDateTime
FROM
[sandbox].[dbo].[TblEmployee]
) AS firstDay
UNION ALL
SELECT
[Employee-ID],
CAST(EndDateTime AS DATE) [Date],
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
0
ELSE
DATEDIFF(minute, DATEADD(day, DATEDIFF(day, 0, EndDateTime), 0), EndDateTime) / 60.0
END HoursWorked,
CASE
WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
0
ELSE
BreakHours / 2
END HoursOnBreak
FROM (
SELECT
[ID],
[Employee-ID],
[Date],
[Coming-time],
[Leaving-time],
[Break(in hours)] AS BreakHours,
CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
CASE
WHEN [Coming-time] <= [Leaving-time] THEN
CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
ELSE
CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
END AS EndDateTime
FROM
[sandbox].[dbo].[TblEmployee]
) AS secondDay
在此之后,您只需进行一些聚合和 whamo。想要按不同时间段进行聚合,只需更改分组方式以符合您的需要。