Search code examples
concatenationgroupingsql-server-2016rollupstuff

SQL Query to group by time and roll up and concatenate string values


I am trying to get a particular format from a group of times and days between two tables.

Database: MeetingTime table has a relationship from MeetingTime.DayOfWeekId (foreign key) to table DayOfWeek.Id (Primary Key). Example Query:

select t.ClassId, d.Name, t.StartTime, t.EndTime
From MeetingTime t
Inner Join DaysOfWeek d on d.Id = t.DayOfWeekId
Where t.classId = 8

Results:

enter image description here

My desired results for this set of data would be one row, because the start and end times are the same.

09:00-15:35 M/T/W/Th/F

NOTE, the start and end time above, can be separate columns above, the main goal is display the days of the week for each grouped time.

The monkey wrench is that the times can be completely different or the same. For example this data set: enter image description here

I would want displayed in 2 rows:

07:35-14:15 M/T/W

08:00-14:15 Th/F

And finally, this dataset where all times are different:

enter image description here

Would display in 5 rows:

13:48-14:48 M

15:48-16:48 T

05:49-23:53 W

14:49-16:49 Th

13:49-16:49 F

I haven't had much success with grouping the times. I did figure out how to concatenate the days of the week rolling the days up into one column using the 'Stuff' Operator, but didn't get anywhere with the grouping of the start and end time coupled with this yet.

Concatenating and rolling up days:

   STUFF((SELECT '/ ' + 
      (CASE
            WHEN d.[Name] = 'Thursday' THEN SUBSTRING(d.[Name], 1, 2)
            WHEN d.[Name] = 'Sunday' THEN 'U'
            WHEN d.[Name] != '' THEN SUBSTRING(d.[Name], 1, 1)
            ELSE NULL
        END)
      FROM MeetingTime m
        Inner Join [DayOfWeek] d on d.Id = m.DayOfWeekId
        Where m.ClassId = class.Id
      FOR XML PATH('')), 1, 1, '') [ClassSchedule]

I'm also not opposed to just returning the rows and handling the data manipulation in C# code, but wanted to see if SQL could handle it.


Solution

  • I was able to get this working. Here is the query:

    select 
    t.ClassId, 
    t.StartTime, 
    t.EndTime,
    STUFF((SELECT '/' + (CASE
                            WHEN w.[Name] = 'Thursday' THEN SUBSTRING(w.[Name], 1, 2)
                            WHEN w.[Name] = 'Sunday' THEN 'U'
                            WHEN w.[Name] != '' THEN SUBSTRING(w.[Name], 1, 1)
                            ELSE NULL
                        END)
          From MeetingTime s
          Inner Join DayOfWeek w on w.Id = s.DayOfWeekId
          Where s.classId = 7 and s.DayOfWeekId > 0 
          and s.StartTime = t.StartTime 
          and s.EndTime = t.EndTime
          FOR XML PATH('')), 1, 1, '') [ClassSchedule]
    From MeetingTime t
    Inner Join DayOfWeek d on d.Id = t.DayOfWeekId
    Where t.classId = 7 and t.DayOfWeekId > 0
    Group by t.StartTime, t.EndTime, t.ClassId
    

    Obviously hardcoded Id you would want to create a variable.

    Results where the start and end time are all the same: enter image description here

    Some times the same and some different: enter image description here

    Some times the same and some different with days not in order: enter image description here

    Times all different:

    enter image description here

    Times with only Mon/Wed/Fri.

    enter image description here

    I feel pretty good about this, except I'd like to fix the order of the above result image where all times are different and the days are not in chronological order.