Existing Tables: Projects - projectID - name - rate Shifts - shiftID (PK) - projectID (FK) - name - startTime - rate Weekdays - weekdayID (PK) - weekday Shifts_Weekdays - shifts_weekdaysID (PK) - shiftID - weekdayID - addition WorkSegments - worksegmentID (PK) - userID - startTime - endTime Table [Weekdays] contains 7 entries, one per day (e.q. Monday, Tuesday, Wednesday, etc..) Table [Shifts] contains 2 entries: 1. name: "Day Shift", startTime: "1900-01-01 08:00:00.000" rate: 10 2. name: "Night Shift", startTime: "1900-01-01 22:00:00.000" rate: 12 Table [Shifts_Weekdays] contains all entries so that each shift is bound to each day, in other words both shifts are active each day of the week == Requirement == I need to find a way to apply the correct rate to the work segments. (UDF or similar) Ex. On Monday, user #1 worked from 6am - 5pm --> rate from shift #2 ($12) should apply for 2 hrs --> rate from shift #1 ($10) should apply for 9 hrs Please note that shifts have startTime only, meaning the end of shift is beginning of the next shift ======================================================== [Shifts] ID projectID userID shift startTime rate 1 1 1 Day Shift 1900-01-01 08:00:00.000 10.00 2 1 1 Night Shift 1900-01-01 22:00:00.000 12.00 [Weekdays] ID weekday code 1 Monday mon 2 Tuesday tue 3 Wednesday wed 4 Thursday thur 5 Friday fri 6 Saturday sat 7 Sunday sun [Shifts_Weekdays] ID shiftID weekdayID addition 231 2 1 0 232 2 2 0 233 2 3 0 234 2 4 0 235 2 5 0 236 2 6 10% 237 2 7 10% 260 1 1 0 261 1 2 0 262 1 3 0 263 1 4 0 264 1 5 0 265 1 6 10% 266 1 7 10% [Projects] ID companyID project rate 1 2 Truck Driving 8.2 [WorkSegments] ID userID projectID startTime endTime 1 1 1 2015-03-10 07:00:00.000 2015-03-10 10:25:00.000 2 1 1 2015-03-10 10:45:00.000 2015-03-10 17:00:00.000 3 1 1 2015-03-10 19:05:00.000 2015-03-10 22:15:00.000 4 1 1 2015-03-11 07:00:00.000 2015-03-11 10:00:00.000 5 1 1 2015-03-11 10:30:00.000 2015-03-11 17:00:00.000 6 1 1 2015-03-11 19:05:00.000 2015-03-11 22:15:00.000 ======================================================== ADDITIONAL [WorkSegments] DATA THAT BREAKS THE FUNCTIONALITY ======================================================== ID startTime endTime 83 2015-04-20 12:00:00.000 2015-04-21 00:30:00.000 84 2015-04-21 15:30:00.000 2015-04-22 03:45:00.000 85 2015-04-23 13:45:00.000 2015-04-24 04:00:00.000 86 2015-04-24 15:30:00.000 2015-04-25 03:45:00.000 87 2015-04-25 12:00:00.000 2015-04-26 03:00:00.000
答案 0 :(得分:0)
以下是使用函数计算工作段工资的解决方案:
--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
注意:如果在一次考虑多个工作段的查询中使用此函数,则该过程效率低下。在这种情况下,需要不同的实施方案。此外,通过将此函数的某些部分分解为定期更新表,可以实现性能提升。