如何计算 sql 中的员工工作时间
How to calculate employees working hours in sql
我公司的营业时间是7:30到18点,13:00到15:00是午餐时间。
重要的部分是关于午餐时间,根本不应该算作员工的工作时间。
- 因此假设员工在 8:30 开始工作并在 15:00 下班,因此应该为他计算 4:30 小时的时间。实际上我想在不同情况下从员工的出勤时间中删除午餐时间:
我的字段都是 Sql 中的 Time(7) 格式。
我发布这段代码来解决你的问题
TimeSpan? ActualTime = obj.ExitTime - obj.EnterTime;
string[] LunchTime = obj.lunchTimeString.Split('-');
TimeSpan ActualLunchTime = TimeSpan.Parse(LunchTime[1].Trim()) - TimeSpan.Parse(LunchTime[0].Trim());
TimeSpan WorkingHours = TimeSpan.Parse(ActualTime.ToString().Trim()) - TimeSpan.Parse(ActualLunchTime.ToString().Trim());
在这段代码中我所做的是:
- 您必须计算包括午餐时间在内的实际时间。
- 你的第二个问题是午餐时间,它的格式是 (13:00 - 15:00)。所以,我不能在 sql 中使用 time(7) 所以把它当作 varchar 然后用 '-' 分割它。
- 然后用它计算总的午餐时间。
- 最后减去实际时间和实际午餐时间就得到你的工作时间。
这是 SQL 服务器中的一种方法。它从根本上将每个人的轮班分成两个 - Pre-Lunch 和 Post-Lunch。当轮班进入(或过去)午餐时,它使用午餐时间作为界限。
我还用位(例如 CTE、sub-query 等)编写了它,因此您可以 运行 单独查看它们的作用。您可能需要为自己的数据库结构等更新它。
-- Data setup
CREATE TABLE #WorkLog (WorkDate date, StartTime time, EndTime time, StartLunch time, EndLunch time)
INSERT INTO #WorkLog (WorkDate, StartTime, EndTime, StartLunch, EndLunch) VALUES
('2020-09-01', '07:30', '18:00', '13:00', '15:00'),
('2020-09-02', '12:00', '15:00', '13:00', '15:00'),
('2020-09-03', '15:00', '18:00', '13:00', '15:00'),
('2020-09-04', '08:30', '15:00', '13:00', '15:00')
SELECT * FROM #WorkLog
------
-- Find times worked
; WITH PreLunchTimes AS
(SELECT WorkDate,
StartTime AS StartTime,
CASE WHEN EndTime < StartLunch THEN EndTime ELSE StartLunch END AS EndTime
FROM #WorkLog
WHERE StartTime < StartLunch
),
PostLunchTimes AS
(SELECT WorkDate,
CASE WHEN StartTime > EndLunch THEN StartTime ELSE EndLunch END AS StartTime,
EndTime AS EndTime
FROM #WorkLog
WHERE EndTime > EndLunch
)
SELECT WorkDate, SUM(Elapsed_Mins) AS Total_Work_Mins, CAST(DATEADD(minute, SUM(Elapsed_Mins), 0) AS time) AS Total_work_time
FROM (SELECT WorkDate, DATEDIFF(minute, StartTime, EndTime) AS Elapsed_Mins
FROM PreLunchTimes
UNION ALL
SELECT WorkDate, DATEDIFF(minute, StartTime, EndTime) AS Elapsed_Mins
FROM PostLunchTimes
) AS A
GROUP BY WorkDate
ORDER BY WorkDate
这是一个db<>fiddle
问题:
- 如果您的班次超过午夜,您需要添加适当的代码来处理。
- 如果 all 午餐时间是 13:00 到 15:00,那么您可以将它们设置为变量(例如,@LunchStart 和 @LunchEnd)而不是将它们存储在数据中。
唉,你忘了给我们你的数据库结构 table。您的 EnterTime 和 ExitTime 是完整的 DateTimes,还是您在员工开始和停止工作的那一天有一个 WorkDay 和一个 TimeOfDay?
另外你忘了给我们你的table结构,我不知道你是怎么和你的数据库通信的,是用entity framework,还是传统的SQL,以及你说的 SQL 方言。有些方言可以进行 DateTime 计算,有些则不能。
MadReflection的评论是明智的:如果你可以改变你的数据库,考虑记住午餐前班次的开始和停止DateTime以及午餐后班次的开始/停止DateTime。通过这种方式,您可以为未来的变化做好准备,例如每位员工的不同午餐时间,甚至是员工每天的不同午餐时间。如果你想在DateTime中进入和退出,你甚至可以让一个Employee在除夕开始工作并在不同的年份停止工作。
幸运的是,您不必让数据库计算午餐时间,因为您可以在本地计算午餐时间。这比让数据库管理系统计算它们并将结果从 DBMS 传输到本地进程要快。
在本地计算午餐时间只会减去两个 long,这比传输数据所需的代码要快得多。
所以我们不能给你SQL。您必须自己编写程序来获取数据。
您需要一种方法来获取一段时间内每个员工或所有员工的 EnterTime / ExitTime。结果类似于:
class WorkingTimes
{
public EmployeeInformation Employee {get; set;}
public DateTime EnterTime {get; set;}
public DateTime ExitTime {get; set;}
}
以及为每位员工或所有员工获取此信息的方法:
IEnumerable<WorkingTimes> FetchWorkingTimes(DateTime beginPeriod, DateTime endPeriod)
{
...
}
IEnumerable<WorkingTimes> FetchWorkingTimes(int employeeId,
DateTime beginPeriod, DateTime endPeriod)
{
...
}
要将获取的数据转换为您想要的格式,一个简单的 LINQ 方法即可:
class WorkingTimeExt
{
// TODO: if needed: add employee information
public System.DayOfWeek DayOfWeek => this.EnterTime.DayOfWeek;
public DateTime EnterTime {get; set;}
public DateTime ExitTime {get; set;}
public DateTime StartLunch {get; set;}
public DateTime StopLunch {get; set;}
public string Lunch => String.Format("...", this.StartLunch, this.StopLunch);
public TimeSpan WorkingTime => this.StartLunch - this.EnterTime
+ this.ExitTime - this.StopLunch;
}
WorkingTime的计算速度很快,就是加了一些longs
TimeSpan defaultLunchStart = TimeSpan.FromHours(13.00);
TimeSpan defaultLunchEnd = TimeSpan.FromHours(15.00);
IEnumerable<WorkingTimes> fetchedWorkingTimes = ...
IEnumerable<WorkingTimesExt> result = fetchedWorkingTimes
.Select(fetchedTimes => new WorkingTimesExt
{
EnterTime = fetchedWorkingTimes.EnterTime,
ExitTime = fetchedWorkingTimes.ExitTime,
StartLunchTime = fetchedWorkingTime.EnterTime.Date + defaultLunchStart,
StopLunchTime = fetchedWorkingTime.ExitTime.Data + defaultLunchStop,
});
如果您需要定期执行此操作,请考虑为其创建一个扩展方法:
IEnumerable<WorkingTimesExt> result = ToExtendedWorkingTimes(
this IEnumerable<WorkingTimes> source,
TimeSpan timeLunchStart,
TimeSpan timeLunchStop)
{
return source.Select(fetchedTimes => new WorkingTimesExt
{
EnterTime = fetchedWorkingTimes.EnterTime,
ExitTime = fetchedWorkingTimes.ExitTime,
StartLunchTime = fetchedWorkingTime.EnterTime.Date + timeLunchStart,
StopLunchTime = fetchedWorkingTime.ExitTime.Data + timeLunchStop,
});
}
另一个工作时间转换为HH:MM格式的有效选项:
CREATE TABLE t1 (dow VARCHAR(10), workstart TIME, workend TIME);
INSERT INTO t1 VALUES
('Monday', '7:30', '18:00'),
('Tuesday', '12:00', '15:00'),
('Wednesday', '15:00', '18:00'),
('Thursday', '8:30', '15:00');
WITH
lunch(lunchstart, lunchend) AS (
SELECT CAST('13:00' AS TIME), CAST('15:00' AS TIME)
),
attendances AS (
SELECT *,
CASE WHEN workstart <= lunchstart THEN
CASE WHEN workend <= lunchstart THEN
DATEDIFF(minute, workstart, workend)
ELSE
DATEDIFF(minute, workstart, lunchstart)
END
ELSE 0 END --AS morning_shift
+
CASE WHEN workend >= lunchend THEN
CASE WHEN workstart >= lunchend THEN
DATEDIFF(minute, workstart, workend)
ELSE
DATEDIFF(minute, lunchend, workend)
END
ELSE 0 END --AS afternoon_shift
AS total_mins
FROM t1, lunch
)
SELECT *, FORMAT(total_mins / 60 * 100 + total_mins % 60, '#:0#') AS hhmm
FROM attendances;
我公司的营业时间是7:30到18点,13:00到15:00是午餐时间。 重要的部分是关于午餐时间,根本不应该算作员工的工作时间。
- 因此假设员工在 8:30 开始工作并在 15:00 下班,因此应该为他计算 4:30 小时的时间。实际上我想在不同情况下从员工的出勤时间中删除午餐时间:
我的字段都是 Sql 中的 Time(7) 格式。
我发布这段代码来解决你的问题
TimeSpan? ActualTime = obj.ExitTime - obj.EnterTime;
string[] LunchTime = obj.lunchTimeString.Split('-');
TimeSpan ActualLunchTime = TimeSpan.Parse(LunchTime[1].Trim()) - TimeSpan.Parse(LunchTime[0].Trim());
TimeSpan WorkingHours = TimeSpan.Parse(ActualTime.ToString().Trim()) - TimeSpan.Parse(ActualLunchTime.ToString().Trim());
在这段代码中我所做的是:
- 您必须计算包括午餐时间在内的实际时间。
- 你的第二个问题是午餐时间,它的格式是 (13:00 - 15:00)。所以,我不能在 sql 中使用 time(7) 所以把它当作 varchar 然后用 '-' 分割它。
- 然后用它计算总的午餐时间。
- 最后减去实际时间和实际午餐时间就得到你的工作时间。
这是 SQL 服务器中的一种方法。它从根本上将每个人的轮班分成两个 - Pre-Lunch 和 Post-Lunch。当轮班进入(或过去)午餐时,它使用午餐时间作为界限。
我还用位(例如 CTE、sub-query 等)编写了它,因此您可以 运行 单独查看它们的作用。您可能需要为自己的数据库结构等更新它。
-- Data setup
CREATE TABLE #WorkLog (WorkDate date, StartTime time, EndTime time, StartLunch time, EndLunch time)
INSERT INTO #WorkLog (WorkDate, StartTime, EndTime, StartLunch, EndLunch) VALUES
('2020-09-01', '07:30', '18:00', '13:00', '15:00'),
('2020-09-02', '12:00', '15:00', '13:00', '15:00'),
('2020-09-03', '15:00', '18:00', '13:00', '15:00'),
('2020-09-04', '08:30', '15:00', '13:00', '15:00')
SELECT * FROM #WorkLog
------
-- Find times worked
; WITH PreLunchTimes AS
(SELECT WorkDate,
StartTime AS StartTime,
CASE WHEN EndTime < StartLunch THEN EndTime ELSE StartLunch END AS EndTime
FROM #WorkLog
WHERE StartTime < StartLunch
),
PostLunchTimes AS
(SELECT WorkDate,
CASE WHEN StartTime > EndLunch THEN StartTime ELSE EndLunch END AS StartTime,
EndTime AS EndTime
FROM #WorkLog
WHERE EndTime > EndLunch
)
SELECT WorkDate, SUM(Elapsed_Mins) AS Total_Work_Mins, CAST(DATEADD(minute, SUM(Elapsed_Mins), 0) AS time) AS Total_work_time
FROM (SELECT WorkDate, DATEDIFF(minute, StartTime, EndTime) AS Elapsed_Mins
FROM PreLunchTimes
UNION ALL
SELECT WorkDate, DATEDIFF(minute, StartTime, EndTime) AS Elapsed_Mins
FROM PostLunchTimes
) AS A
GROUP BY WorkDate
ORDER BY WorkDate
这是一个db<>fiddle
问题:
- 如果您的班次超过午夜,您需要添加适当的代码来处理。
- 如果 all 午餐时间是 13:00 到 15:00,那么您可以将它们设置为变量(例如,@LunchStart 和 @LunchEnd)而不是将它们存储在数据中。
唉,你忘了给我们你的数据库结构 table。您的 EnterTime 和 ExitTime 是完整的 DateTimes,还是您在员工开始和停止工作的那一天有一个 WorkDay 和一个 TimeOfDay?
另外你忘了给我们你的table结构,我不知道你是怎么和你的数据库通信的,是用entity framework,还是传统的SQL,以及你说的 SQL 方言。有些方言可以进行 DateTime 计算,有些则不能。
MadReflection的评论是明智的:如果你可以改变你的数据库,考虑记住午餐前班次的开始和停止DateTime以及午餐后班次的开始/停止DateTime。通过这种方式,您可以为未来的变化做好准备,例如每位员工的不同午餐时间,甚至是员工每天的不同午餐时间。如果你想在DateTime中进入和退出,你甚至可以让一个Employee在除夕开始工作并在不同的年份停止工作。
幸运的是,您不必让数据库计算午餐时间,因为您可以在本地计算午餐时间。这比让数据库管理系统计算它们并将结果从 DBMS 传输到本地进程要快。
在本地计算午餐时间只会减去两个 long,这比传输数据所需的代码要快得多。
所以我们不能给你SQL。您必须自己编写程序来获取数据。
您需要一种方法来获取一段时间内每个员工或所有员工的 EnterTime / ExitTime。结果类似于:
class WorkingTimes
{
public EmployeeInformation Employee {get; set;}
public DateTime EnterTime {get; set;}
public DateTime ExitTime {get; set;}
}
以及为每位员工或所有员工获取此信息的方法:
IEnumerable<WorkingTimes> FetchWorkingTimes(DateTime beginPeriod, DateTime endPeriod)
{
...
}
IEnumerable<WorkingTimes> FetchWorkingTimes(int employeeId,
DateTime beginPeriod, DateTime endPeriod)
{
...
}
要将获取的数据转换为您想要的格式,一个简单的 LINQ 方法即可:
class WorkingTimeExt
{
// TODO: if needed: add employee information
public System.DayOfWeek DayOfWeek => this.EnterTime.DayOfWeek;
public DateTime EnterTime {get; set;}
public DateTime ExitTime {get; set;}
public DateTime StartLunch {get; set;}
public DateTime StopLunch {get; set;}
public string Lunch => String.Format("...", this.StartLunch, this.StopLunch);
public TimeSpan WorkingTime => this.StartLunch - this.EnterTime
+ this.ExitTime - this.StopLunch;
}
WorkingTime的计算速度很快,就是加了一些longs
TimeSpan defaultLunchStart = TimeSpan.FromHours(13.00);
TimeSpan defaultLunchEnd = TimeSpan.FromHours(15.00);
IEnumerable<WorkingTimes> fetchedWorkingTimes = ...
IEnumerable<WorkingTimesExt> result = fetchedWorkingTimes
.Select(fetchedTimes => new WorkingTimesExt
{
EnterTime = fetchedWorkingTimes.EnterTime,
ExitTime = fetchedWorkingTimes.ExitTime,
StartLunchTime = fetchedWorkingTime.EnterTime.Date + defaultLunchStart,
StopLunchTime = fetchedWorkingTime.ExitTime.Data + defaultLunchStop,
});
如果您需要定期执行此操作,请考虑为其创建一个扩展方法:
IEnumerable<WorkingTimesExt> result = ToExtendedWorkingTimes(
this IEnumerable<WorkingTimes> source,
TimeSpan timeLunchStart,
TimeSpan timeLunchStop)
{
return source.Select(fetchedTimes => new WorkingTimesExt
{
EnterTime = fetchedWorkingTimes.EnterTime,
ExitTime = fetchedWorkingTimes.ExitTime,
StartLunchTime = fetchedWorkingTime.EnterTime.Date + timeLunchStart,
StopLunchTime = fetchedWorkingTime.ExitTime.Data + timeLunchStop,
});
}
另一个工作时间转换为HH:MM格式的有效选项:
CREATE TABLE t1 (dow VARCHAR(10), workstart TIME, workend TIME);
INSERT INTO t1 VALUES
('Monday', '7:30', '18:00'),
('Tuesday', '12:00', '15:00'),
('Wednesday', '15:00', '18:00'),
('Thursday', '8:30', '15:00');
WITH
lunch(lunchstart, lunchend) AS (
SELECT CAST('13:00' AS TIME), CAST('15:00' AS TIME)
),
attendances AS (
SELECT *,
CASE WHEN workstart <= lunchstart THEN
CASE WHEN workend <= lunchstart THEN
DATEDIFF(minute, workstart, workend)
ELSE
DATEDIFF(minute, workstart, lunchstart)
END
ELSE 0 END --AS morning_shift
+
CASE WHEN workend >= lunchend THEN
CASE WHEN workstart >= lunchend THEN
DATEDIFF(minute, workstart, workend)
ELSE
DATEDIFF(minute, lunchend, workend)
END
ELSE 0 END --AS afternoon_shift
AS total_mins
FROM t1, lunch
)
SELECT *, FORMAT(total_mins / 60 * 100 + total_mins % 60, '#:0#') AS hhmm
FROM attendances;