Reputation: 829
I've got the following 3 tables.
Staff:
Name, LastName, SSN
Course Instance:
Course, Year, Period
Attended By:
Course, SSN, Year, Period, Hours
I'm trying to make a stored procedure that returns a table with a matrix over the first 4 course instances in a given year (taken as parameter to the procedure).
The returned matrix needs to look something like this:
Name | LastName | Course | Course | Course | Course
The four different Course
are the first four courses from a given year, so what I've got now is this code to find those courses:
DECLARE myCursor CURSOR FOR
SELECT top 4 Course
FROM Course Instance
WHERE Year = @year
ORDER by Period
The rest of the matrix should just get the values from the tables above somehow, basically every Staff member that has Attended any of the Courses should be in the matrix with correct information. And under each Course
column I want the number of hours from the attending staff.
Sample tables
Staff:
Name | LastName | SSN
Steve Lastname 234
Pete Steven 132
Course Instance:
Course | Year | Period
DVA123 2013 1
DVA222 2014 2
Attended by:
Course | SSN | Year | Period | Hours
DVA123 234 2013 1 200
DVA222 132 2014 2 50
Expected output from this:
Name | LastName | DVA123 | DVA222 | nothing | nothing
Pete Steven 200
Steve Lastname 50
Upvotes: 1
Views: 784
Reputation: 69759
I am unsure of exactly how CourseInstance links to attended by, but you can achive this using PIVOT and ROW_NUMBER:
WITH Data AS
( SELECT s.Name,
s.LastName,
s.SSN,
ci.Course,
CourseNum = ROW_NUMBER() OVER(PARTITION BY s.SSN ORDER BY ci.Period)
FROM CourseInstance ci
INNER JOIN AttendedBy a
ON a.Course = ci.Course
AND a.Year = ci.Year
AND a.Period = ci.Period
INNER JOIN Staff s
ON s.SSN = a.SSN
WHERE ci.Year = @year
)
SELECT pvt.Name,
pvt.LastName,
Course1 = pvt.[1],
Course2 = pvt.[2],
Course3 = pvt.[3],
Course4 = pvt.[4]
FROM DATA
PIVOT
( MAX(Course)
FOR CourseNum IN ([1], [2], [3], [4])
) pvt;
It uses MAX(Course)
, but this is not really relevant since CourseNum
is unique to each SSN
you are only ever selecting the max of one row anyway. You could just as easily use MIN
.
I do not advocate this approach one bit, but nevertheless, it will get you started should you chose the dynamic SQL approach instead of doing this in the presentation layer:
DECLARE @Courses NVARCHAR(MAX) = STUFF((SELECT TOP 4 ',' + QUOTENAME(course)
FROM ( SELECT course, Period, SortOrder = 0
FROM CourseInstance
WHERE Year = @Year
UNION ALL
-- THIS IS REQUIRED TO FILL GAPS WHERE THERE AREN'T ENOUGH COURSES
SELECT TOP 4
'Nothing' + CAST(ROW_NUMBER() OVER(ORDER BY object_id) AS CHAR(1)),
0,
1
FROM sys.all_objects
) t
ORDER BY SortOrder, Period, course
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE @SQL NVARCHAR(MAX) =
' SELECT Name, LastName, ' + @Courses + '
FROM ( SELECT s.Name, s.LastName, s.SSN, ci.Course, a.Hours
FROM AttendedBy a
INNER JOIN Staff s
ON s.SSN = a.SSN
INNER JOIN CourseInstance ci
ON a.Course = ci.Course
AND a.Year = ci.Year
AND a.Period = ci.Period
) d
PIVOT
( SUM(Hours)
FOR Course IN (' + @Courses + ')
) pvt;';
EXECUTE SP_EXECUTESQL @SQL;
Upvotes: 2