I think you will have to stick with unions, because unions are exactly how to create results like the ones you do. The purpose of combining is to combine sets of strings (with or without conditions), and your target result is nothing more than a combination of subsets of strings.
However, if most questions always have single answers, you can significantly reduce the number of associations needed. The idea is to combine only groups with multiple answers in separate sets of rows. As for elements with one answer, they are combined only as part of the entire data set of the target objects.
An example should better illustrate what I could poorly describe verbally. Assuming the source data there are two groups with many answers, 'q03' and 'q06' (in fact, here is the source table:
DECLARE @testTable AS TABLE( userId int, itemName varchar(50), itemValue varchar(255) ); INSERT INTO @testTable SELECT 1, 'q01', '1-q01 Answer' UNION SELECT 1, 'q02', '1-q02 Answer' UNION SELECT 1, 'q03', '1-q03 Answer 1' UNION SELECT 1, 'q03', '1-q03 Answer 2' UNION SELECT 1, 'q03', '1-q03 Answer 3' UNION SELECT 1, 'q04', '1-q04 Answer' UNION SELECT 1, 'q05', '1-q05 Answer' UNION SELECT 1, 'q06', '1-q06 Answer 1' UNION SELECT 1, 'q06', '1-q06 Answer 2' UNION SELECT 1, 'q06', '1-q06 Answer 3' UNION SELECT 2, 'q01', '2-q01 Answer' UNION SELECT 2, 'q02', '2-q02 Answer' UNION SELECT 2, 'q03', '2-q03 Answer 1' UNION SELECT 2, 'q03', '2-q03 Answer 2' UNION SELECT 2, 'q04', '2-q04 Answer' UNION SELECT 2, 'q05', '2-q05 Answer' UNION SELECT 2, 'q06', '2-q06 Answer 1' UNION SELECT 2, 'q06', '2-q06 Answer 2' ;
which is similar to the table in the original message, but with the added elements 'q06' ), the resulting script could be like this:
WITH ranked AS ( SELECT *, rn = ROW_NUMBER() OVER (PARTITION BY userId, itemName ORDER BY itemValue) FROM @testTable ), multiplied AS ( SELECT r.userId, r.itemName, r.itemValue, rn03 = r03.rn, rn06 = r06.rn FROM ranked r03 INNER JOIN ranked r06 ON r03.userId = r06.userId AND r06.itemName = 'q06' INNER JOIN ranked r ON r03.userId = r.userId AND ( r.itemName = 'q03' AND r.rn = r03.rn OR r.itemName = 'q06' AND r.rn = r06.rn OR r.itemName NOT IN ('q03', 'q06') ) WHERE r03.itemName = 'q03' AND r.itemName IN ('q02', 'q03', 'q05', 'q06') ) SELECT userId, rn03, rn06, q02, q03, q05, q06 FROM multiplied PIVOT ( MIN(itemValue) FOR itemName in (q02, q03, q05, q06) ) AS PivotTable