我有一个员工出勤表,如下所示
ID Employee-ID Date Coming-time Leaving-time Break(in hours)
1 1 2016-01-01 08:00:00 18:30:00 0,50
2 1 2016-01-02 20:00:00 08:00:00 1,50
3 1 2016-01-03 18:30:00 06:00:00 1,50
4 1 2016-01-04 08:00:00 18:30:00 0,00
如何在没有休息的情况下计算每个员工的总工作时间.....
问题在于DATEDIFF几小时没有给我半小时的真正价值,例如以下查询:
ID Employee-ID Month TotalHours WorkingTime
1 1 12 10 9,5
2 1 12 12 10,5
3 1 12 12 10,5
4 1 12 10 10
正如您所看到的,TotalHours错误,其值应该是这样的
TotalHours
10,5
12
11,5
10,5
我正在尝试的查询是这样的:(感谢答案和评论的帮助......)
(SELECT ID, Employee-ID, MONTH(Date) AS Month,
CASE
WHEN Coming-time > Leaving-time THEN DATEDIFF(HOUR, Leaving-time, Coming-time)
WHEN Coming-time <= Leaving-time THEN DATEDIFF(HOUR, Coming-time, Leaving-time)
END AS TotalHours,
CASE
WHEN Coming-time > Leaving-time THEN DATEDIFF(HOUR, Leaving-time, Coming-time) - Break
WHEN Coming-time <= Leaving-time THEN DATEDIFF(HOUR, Coming-time, Leaving-time) - Break
END as WorkingTime
FROM TblEmployee)
如何获得总时数的真实值?
答案 0 :(得分:1)
#include <iostream>
#include <memory>
#include <vector>
class Item
{
public:
virtual const char* type() const = 0;
};
class Weapon : public Item
{
public:
const char* type() const override { return "Weapon"; }
};
class Ranged : public Weapon
{
public:
const char* type() const override { return "Ranged"; }
};
class Melee : public Weapon
{
public:
const char* type() const override { return "Melee"; }
};
class Apparel : public Item
{
public:
const char* type() const override { return "Apparel"; }
};
int main()
{
std::vector<std::unique_ptr<Item>> items;
items.emplace_back(new Apparel());
items.emplace_back(new Ranged());
items.emplace_back(new Melee());
std::cout << "Inventory Types:\n";
for (const auto& it: items)
std::cout << it->type() << '\n';
}
这个想法是这样的。
答案 1 :(得分:1)
你曾经看过冰山海报,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每天休息一半。
警告:假设:
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。想要按不同时间段汇总,只需更改组以符合您的需要。
答案 2 :(得分:0)
SELECT [Employee-ID] as employee_id,
MONTH(Date) as month,
SUM(DATEDIFF(MINUTE, [Coming-time], [Leaving-time]) / 60 - [Break]) as working_time
FROM table_name
GROUP BY [Employee-ID],
MONTH(Date)