Recently I have been working on a clinical trial application and needed a way to export all of the data. The client wants the questions to be displayed horizontally, but the data is stored vertically. This is the structure of the data:

Pretty simple schema, the issue is here. This is what they want it to look like:

At first this was a little intimidating, because there are different types of questions in separate tables that would need to be combined and then transposed together. I decided to use the PIVOT function. Now there was an another requirement that needed to be addressed, the columns had to be sorted. This took a little bit of data massaging to get it in the correct pre-pivot format.
To set the stage I have a number of pre-processing steps that I won’t show here, but just know that those steps result in temp table #data. Now that I have all of the data in place, I need to order the questions so that they pivot correctly:
-- PRE-PIVOT PROCESSING
SELECT
[CaseId] ,
[Title],
[Value]
INTO #ordered
FROM #data
ORDER BY
[CaseId],
[Title]
Here comes the interesting part. I need to extract all of the columns into a comma delimited string for use in the pivot. For this I need a VARCHAR to hold the delimited fields. Now I can take the data from the #ordered table and generate the fields:
DECLARE @ColumnName AS NVARCHAR(MAX)
SELECT @ColumnName= ISNULL(@ColumnName + ',','')
+ QUOTENAME([Title])
FROM (SELECT DISTINCT [Title] FROM #ordered ) AS c ORDER BY [Title]
There is another function in there that I didn’t know about before I needed to do this: QUOTENAME. This will take the string and wrap it in square brackets. This allows for strings with spaces to be used for a column name. At this point, we have the @ColumnName variable set with a delimited set of question titles.
Now that we have our columns set, we need to generate the sql that will be executed. For this we need to get the data. We will take use a CTE to collect the data with the new list of columns and pump that into the PIVOT function. The PIVOT function take the aggregate that you want applied to the value field and the column that you want to PIVOT, in my case the @ColumnName array. Lastly, I need to have the rows grouped by the unique CaseId. In the end it looks like this:
DECLARE @query NVARCHAR(MAX);
SET @query = ';WITH p ([CaseId],'+ @ColumnName +') as (SELECT [CaseId],' + @ColumnName + ' from
(
SELECT
[CaseId],
[Title],
[Value]
FROM
#ordered
) x PIVOT (MAX([Value]) for [Title] in ('+@ColumnName+')) p GROUP BY [CaseId],'+@ColumnName+') SELECT * FROM p '
EXECUTE (@query);
When we execute these queries, we end up with the result that we want:

One note, these values above are strings in the data, so an aggregate does not make sense, so you can use the MAX function to get the exact value.


