这是一个使用函数计算工作段工资的解决方案:
--Build schema
CREATE TABLE dbo.Shifts
(
ID INT PRIMARY KEY
,projectID INT
,userID INT
,shift VARCHAR(25)
,startTime TIME
,rate MONEY
)
INSERT INTO dbo.Shifts
VALUES
(1 ,1 ,1 ,'Day Shift' ,'08:00' ,10.00)
,(2 ,1 ,1 ,'Night Shift' ,'22:00' ,12.00)
GO
CREATE TABLE dbo.Weekdays
(
ID INT PRIMARY KEY
,weekday VARCHAR(25)
,code VARCHAR(5)
)
INSERT INTO dbo.Weekdays
VALUES
(1 ,'Monday' ,'mon')
,(2 ,'Tuesday' ,'tue')
,(3 ,'Wednesday' ,'wed')
,(4 ,'Thursday' ,'thur')
,(5 ,'Friday' ,'fri')
,(6 ,'Saturday' ,'sat')
,(7 ,'Sunday' ,'sun')
GO
CREATE TABLE dbo.Shifts_Weekdays
(
ID INT PRIMARY KEY
,shiftID INT
,weekdayID INT
,absoluteIncrease MONEY
,percentIncrease FLOAT
)
INSERT INTO dbo.Shifts_Weekdays
VALUES
(231 ,2 ,1 ,0.0 ,0)
,(232 ,2 ,2 ,0.0 ,0)
,(233 ,2 ,3 ,0.0 ,0)
,(234 ,2 ,4 ,0.0 ,0)
,(235 ,2 ,5 ,0.0 ,0)
,(236 ,2 ,6 ,0.1 ,0)
,(237 ,2 ,7 ,0.1 ,0)
,(260 ,1 ,1 ,0.0 ,0)
,(261 ,1 ,2 ,0.0 ,0)
,(262 ,1 ,3 ,0.0 ,0)
,(263 ,1 ,4 ,0.0 ,0)
,(264 ,1 ,5 ,0.0 ,0)
,(265 ,1 ,6 ,0.1 ,0)
,(266 ,1 ,7 ,0.1 ,0)
GO
CREATE TABLE dbo.Projects
(
ID INT PRIMARY KEY
,companyID INT
,project VARCHAR(50)
,rate FLOAT
)
INSERT INTO dbo.Projects
VALUES
(1 ,2 ,'Truck Driving' ,8.2)
GO
CREATE TABLE dbo.WorkSegments
(
ID INT PRIMARY KEY
,userID INT
,projectID INT
,startTime DATETIME2
,endTime DATETIME2
)
INSERT INTO dbo.WorkSegments
VALUES
(1 ,1 ,1 ,'2015-03-10 07:00' ,'2015-03-10 10:25:00.000')
,(2 ,1 ,1 ,'2015-03-10 10:45' ,'2015-03-10 17:00:00.000')
,(3 ,1 ,1 ,'2015-03-10 19:05' ,'2015-03-10 22:15:00.000')
,(4 ,1 ,1 ,'2015-03-11 07:00' ,'2015-03-11 10:00:00.000')
,(5 ,1 ,1 ,'2015-03-11 10:30' ,'2015-03-11 17:00:00.000')
,(6 ,1 ,1 ,'2015-03-11 19:05' ,'2015-03-11 22:15:00.000')
GO
/***********************************************************************************
DESCRIPTION:
Calculates wages based on work segment
NOTES:
Based on question from Stack Overflow:
http://stackoverflow.com/questions/29310072/calculate-pay-rate-based-on-different-shifts-within-24-hrs/
Revision History
2015-03-30 brennan pope created
2015-04-05 brennan pope modified to include shift and segment start times
***********************************************************************************/
ALTER FUNCTION [dbo].[GetWagesByWorkSegment]
(
@WorkSegmentId INT
)
RETURNS @Totals TABLE
(
WorkSegmentID INT
,userID INT
,projectID INT
,ShiftId INT
,WeekdayId INT
,Rate FLOAT
,ShiftStart DATETIME2
,ShiftEnd DATETIME2
,MinutesInShift INT
,GrossShiftPay MONEY
,SegmentStart DATETIME2
,SegmentEnd DATETIME2
,MinutesInSegment INT
,GrossSegmentPay MONEY
)
AS
BEGIN
DECLARE @StartDate DATE = (SELECT DATEADD(DAY,-1,MIN(StartTime)) FROM WorkSegments WHERE ID = @WorkSegmentId);
DECLARE @EndDate DATE = DATEADD(DAY,1,GETDATE());
DECLARE @Dates TABLE
(
ThisDate DATETIME2 PRIMARY KEY
,ThisWeekDay VARCHAR(25)
,UNIQUE(ThisWeekDay,ThisDate)
);
WITH
Dates_CTE AS
(
SELECT
@StartDate AS ThisDate
,DATENAME(DW,@StartDate) AS THisWeekDay
UNION ALL
SELECT
NextDate
,DATENAME(DW,NextDate)
FROM Dates_CTE
CROSS APPLY (VALUES(DATEADD(DAY,1,ThisDate))) NextDates(NextDate)
WHERE NextDate <= @EndDate
)
INSERT INTO @Dates
SELECT ThisDate,ThisWeekDay
FROM Dates_CTE OPTION (MAXRECURSION 0);
DECLARE @ShiftStartDates TABLE
(
ProjectId INT
,StartDate DATETIME2
,Rate FLOAT
,ShiftId INT
,WeekdayId INT
,UNIQUE(StartDate,ProjectId)
)
INSERT INTO @ShiftStartDates
SELECT
P.ID AS ProjectID
,DATEADD(MINUTE, DATEDIFF(MINUTE,'00:00',S.startTime),D.ThisDate) AS startDate
,(CASE WHEN ISNULL(S.rate,0) = 0 THEN P.rate ELSE S.rate END + SW.absoluteIncrease) * (1+SW.percentIncrease) AS Rate
,S.ID AS ShiftId
,W.ID AS WeekdayId
FROM
Projects P
JOIN
Shifts S
ON P.ID = S.projectID
JOIN
Shifts_Weekdays SW
ON S.ID = SW.shiftID
JOIN
Weekdays W
ON SW.weekdayId = W.Id
JOIN
@Dates D
ON W.weekday = D.ThisWeekDay
DECLARE @ShiftDates TABLE
(
ProjectId INT
,StartDate DATETIME2
,EndDate DATETIME2
,Rate FLOAT
,ShiftId INT
,WeekdayId INT
,UNIQUE(StartDate,ProjectId)
)
INSERT INTO @ShiftDates
SELECT
ProjectId
,StartDate
,(SELECT TOP 1 StartDate FROM @ShiftStartDates WHERE StartDate > S.StartDate) AS EndDate
,Rate
,ShiftId
,WeekdayId
FROM @ShiftStartDates S
--Get results
INSERT INTO @Totals
SELECT
WorkSegmentID
,userID
,projectID
,ShiftId
,WeekdayId
,Rate AS ShiftRate
,ShiftStart
,ShiftEnd
,MinutesInShift
,(Rate * MinutesInShift)/60 AS GrossShiftPay
,SegmentStart
,SegmentEnd
,MinutesInSegment
,SUM((Rate * MinutesInShift)/60) OVER (PARTITION BY WorkSegmentID) AS GrossSegmentPay
FROM
(
SELECT
WS.ID AS WorkSegmentID
,WS.userID
,WS.projectID
,SD.ShiftId
,SD.WeekdayId
,SD.Rate
,
CASE
WHEN WS.startTime >= SD.StartDate AND WS.endTime >= SD.EndDate THEN WS.startTime
WHEN WS.startTime >= SD.StartDate AND WS.endTime <= SD.EndDate THEN WS.startTime
WHEN WS.startTime <= SD.StartDate AND WS.endTime >= SD.EndDate THEN SD.startDate
WHEN WS.startTime <= SD.StartDate AND WS.endTime <= SD.EndDate THEN SD.startDate
END AS ShiftStart
,CASE
WHEN WS.startTime >= SD.StartDate AND WS.endTime >= SD.EndDate THEN SD.endDate
WHEN WS.startTime >= SD.StartDate AND WS.endTime <= SD.EndDate THEN WS.endTime
WHEN WS.startTime <= SD.StartDate AND WS.endTime >= SD.EndDate THEN SD.endDate
WHEN WS.startTime <= SD.StartDate AND WS.endTime <= SD.EndDate THEN WS.endTime
END AS ShiftEnd
,CASE
WHEN WS.startTime >= SD.StartDate AND WS.endTime >= SD.EndDate THEN DATEDIFF(MINUTE,WS.startTime,SD.endDate)
WHEN WS.startTime >= SD.StartDate AND WS.endTime <= SD.EndDate THEN DATEDIFF(MINUTE,WS.startTime,WS.endTime)
WHEN WS.startTime <= SD.StartDate AND WS.endTime >= SD.EndDate THEN DATEDIFF(MINUTE,SD.startDate,SD.endDate)
WHEN WS.startTime <= SD.StartDate AND WS.endTime <= SD.EndDate THEN DATEDIFF(MINUTE,SD.startDate,WS.endTime)
END AS MinutesInShift
,WS.startTime AS SegmentStart
,WS.endTime AS SegmentEnd
,DATEDIFF(MINUTE,WS.startTime,WS.endTime) AS MinutesInSegment
FROM
WorkSegments WS
JOIN
@ShiftDates SD
ON
WS.projectID = SD.ProjectId
AND WS.endTime > SD.StartDate
AND WS.startTime < SD.EndDate
WHERE WS.ID = @WorkSegmentId
) PayIntervals
RETURN;
END
GO
下面是如何使用表值函数:
SELECT * FROM dbo.GetWagesByWorkSegment([WorkSegmentId])
WorkSegmentId = 6 的结果:
WorkSegmentID userID projectID ShiftId WeekdayId Rate ShiftStart ShiftEnd MinutesInShift GrossShiftPay SegmentStart SegmentEnd MinutesInSegment GrossSegmentPay
6 1 1 1 3 10 2015-03-11 19:05:00.0000000 2015-03-11 22:00:00.0000000 175 29.1667 2015-03-11 19:05:00.0000000 2015-03-11 22:15:00.0000000 190 32.1667
6 1 1 2 3 12 2015-03-11 22:00:00.0000000 2015-03-11 22:15:00.0000000 15 3.00 2015-03-11 19:05:00.0000000 2015-03-11 22:15:00.0000000 190 32.1667
注意:如果您在一次考虑多个工作段的查询中使用此函数,则该过程效率低下。在这种情况下,需要一种不同的实现方式。此外,可以通过将此函数的某些部分分解为定期更新的表来实现性能提升。