Thanks in advance. I’m basically trying to run a SQL query so that the results are 1:1:1:1:Many for StaffID:Name:Floor:Date:Shifts.
Here is my initial query and example results:
SELECT ST.STAFFNUM [StaffID], ST.FULLNAME [Name], ST.AREA [Floor], CONVERT(VARCHAR(10), TS.EVENTDATE, 103) [Date], LEFT(CONVERT(VARCHAR(10), TS.SHIFTSTART, 8), 5) [ShiftStart], LEFT(CONVERT(VARCHAR(10), TS.SHIFTEND, 8), 5) [ShiftEnd] FROM TIMES TS LEFT JOIN STAFF ST ON TS.STAFFNUM = ST.STAFFNUM WHERE TS.EVENTDATE BETWEEN '2021/01/01' AND '2021/01/01' ORDER BY ST.AREA, ST.FULLNAME, TS.EVENTDATE, TS.SHIFTSTART ; StaffID | Name | Floor | Date | ShiftStart | ShiftEnd ============================================================== 1000 | Andrew | 1 | 01/01/2021 | 06:00 | 14:00 1000 | Andrew | 1 | 01/01/2021 | 14:00 | 15:00 8654 | Belinda | 2 | 01/01/2021 | 06:00 | 14:00 9876 | Craig | 3 | 01/01/2021 | 06:00 | 14:00
I then combined the ShiftStart and ShiftEnd columns with the following to get the further below results:
CONCAT(LEFT(CONVERT(VARCHAR(10), TS.SHIFTSTART, 8), 5),'-',LEFT(CONVERT(VARCHAR(10), TS.SHIFTEND, 8), 5)) [Shift] StaffID | Name | Floor | Date | Shift ==================================================== 1000 | Andrew | 1 | 01/01/2021 | 06:00-14:00 1000 | Andrew | 1 | 01/01/2021 | 14:00-15:00 8654 | Belinda | 2 | 01/01/2021 | 06:00-14:00 9876 | Craig | 3 | 01/01/2021 | 06:00-14:00
What I can’t figure out to do next though is to combine Andrew’s shifts (and anyone else’s multiple shifts on the same Date and Floor etc) like the below:
StaffID | Name | Floor | Date | Shifts ================================================================= 1000 | Andrew | 1 | 01/01/2021 | 06:00-14:00, 14:00-15:00 8654 | Belinda | 2 | 01/01/2021 | 06:00-14:00 9876 | Craig | 3 | 01/01/2021 | 06:00-14:00
NOTE: If someone is transferred to another Floor (occasional), I want to keep that Shift/s on separate rows unique to the Floor so the Floor data can be split up and emailed to that floor’s manager. As long as all data of the rows are identical except for the Shift, I want to combine those rows and list the Shifts. Thanks again!
Advertisement
Answer
As mentioned, you should use the String_Agg function as follows:
SELECT [StaffID], Max([Name]) As Name, [Floor], [Date], String_Agg([Shift], ',') WITHIN GROUP (ORDER BY [Shift]) As Shifts FROM ( SELECT ST.STAFFNUM [StaffID], ST.FULLNAME [Name], ST.AREA [Floor], CONVERT(VARCHAR(10), TS.EVENTDATE, 103) [Date], CONCAT(LEFT(CONVERT(VARCHAR(10), TS.SHIFTSTART, 8), 5),'-',LEFT(CONVERT(VARCHAR(10), TS.SHIFTEND, 8), 5)) [Shift] FROM TIMES TS LEFT JOIN STAFF ST ON TS.STAFFNUM = ST.STAFFNUM WHERE TS.EVENTDATE BETWEEN '2021/01/01' AND '2021/01/01' ORDER BY ST.AREA, ST.FULLNAME, TS.EVENTDATE, TS.SHIFTSTART) As T Group by [StaffID], [Floor], [Date]
For Sql Server 2012, you can try “CTE” with “FOR XML PATH”.
WITH CTE As (SELECT ST.STAFFNUM [StaffID], ST.FULLNAME [Name], ST.AREA [Floor], CONVERT(VARCHAR(10), TS.EVENTDATE, 103) [Date], CONCAT(LEFT(CONVERT(VARCHAR(10), TS.SHIFTSTART, 8), 5),'-',LEFT(CONVERT(VARCHAR(10), TS.SHIFTEND, 8), 5)) [Shift] FROM TIMES TS LEFT JOIN STAFF ST ON TS.STAFFNUM = ST.STAFFNUM WHERE TS.EVENTDATE BETWEEN '2021/01/01' AND '2021/01/01' ORDER BY ST.AREA, ST.FULLNAME, TS.EVENTDATE, TS.SHIFTSTART) SELECT [StaffID], MAX([Name]) AS [Name], [Floor], [Date], STUFF((SELECT ', ' + [Shift] FROM CTE WHERE [StaffID] = T.[StaffID] AND [Floor] = T.[Floor] AND [Date] = T.[Date] FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)'),1,1,'') AS Shifts FROM CTE AS T GROUP BY [StaffID], [Floor], [Date]