Reputation: 9654
I think it would be quickest to show by example, so here goes a simplified version of my problem. I am using SQL Server.
This is the data model
SURVEY QUESTION ANSWER
------ -------- -------
surveyId questionId answerId
questionText surveyId
questionId
answerText
Here's some sample data
SURVEY QUESTION
---------- ------------ --------------
| surveyId | | questionId | questionText |
---------- ------------ --------------
| 1 | | 1 | Name |
| 2 | | 2 | E-Mail |
---------- | 3 | Address |
| 4 | Phone |
------------ --------------
ANSWER
-----------------------------------------------
| answerId | surveyId | questionId | answerText |
-----------------------------------------------
| 1 | 1 | 1 | John |
| 2 | 1 | 2 | [email protected] |
| 3 | 1 | 3 | New York |
| 4 | 1 | 4 | 1112223344 |
| 5 | 2 | 1 | Pete |
| 6 | 2 | 2 | [email protected] |
| 7 | 2 | 3 | Boston |
| 8 | 2 | 4 | 5556667788 |
-----------------------------------------------
Finally, here's what I'd like to select
RESULT
------------------------------
| surveyId | name | email |
------------------------------
| 1 | John | [email protected] |
| 2 | Pete | [email protected] |
------------------------------
I'd like to be able to specify at run time which columns I need in my final table. Right now I can get this result by doing the following
SELECT rName.surveyId, rName.requestor, rEmail.email
FROM (SELECT a.answerText AS name, a.surveyId
FROM answer a INNER JOIN question q ON a.questionId = q.questionId
WHERE a.surveyId = @surveyId AND q.questionText = 'Name') AS rName
INNER JOIN
(SELECT a.answerText AS name, a.surveyId
FROM answer a INNER JOIN question q ON a.questionId = q.questionId
WHERE a.surveyId = @surveyId AND q.questionText = 'E-Mail') AS rEmail
ON rName.questionnaireId = rEmail.questionnaireId
As you can see, it's ugly, and contains duplicate code. Is there a way to do this cleaner?
Upvotes: 0
Views: 148
Reputation: 19
Might not be the "correct" usage of PIVOT, but the following will do the trick (req. SQL2005 or greater):
SELECT surveryId, [1] AS [name], [2] AS [e-mail]
FROM
( SELECT a.surveryId, q.QuestionId, a.answerText
FROM dbo.Question q
INNER JOIN dbo.Answer a ON a.questionId = q.QuestionId
) tmp
PIVOT (
MAX(answerText)
FOR questionId IN ([1], [2])
) PvtTable
Upvotes: 1
Reputation: 117530
select *
from
(
select
s.surveyid,
q.questionText,
a.answerText
from SURVEY as s
cross join QUESTION as q
left outer join ANSWER as a on a.questionId = q.questionId and a.surveyId = s.SurveyId
) as M
pivot
(
min(M.answerText)
for M.questionText in ([Name], [E-Mail], [Address], [Phone])
) as P
Upvotes: 2
Reputation: 247850
SQL Server has a PIVOT
function that is available for this type of query. If you know the values that you want to transform you can hard-code the values:
select surveyid, name, [e-mail]
from
(
select a.answertext,
q.questiontext,
s.surveyid
from survey s
left join answer a
on s.surveyid = a.surveyid
left join question q
on a.questionid = q.questionid
) src
pivot
(
max(answertext)
for questiontext in ([name], [e-mail])
) p
Now if you have an unknown number of values, you can use dynamic sql to pivot
the data:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(questiontext)
from question
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT surveyid, ' + @cols + ' from
(
select a.answertext,
q.questiontext,
s.surveyid
from survey s
left join answer a
on s.surveyid = a.surveyid
left join question q
on a.questionid = q.questionid
) x
pivot
(
max(answertext)
for questiontext in (' + @cols + ')
) p '
execute(@query)
Upvotes: 2
Reputation: 263843
SELECT a.surveyID,
MAX(CASE WHEN c.questionText = 'Name' THEN b.answerText ELSE NULL END) name,
MAX(CASE WHEN c.questionText = 'E-Mail' THEN b.answerText ELSE NULL END) email
FROM Survey a
INNER JOIN Answer b
ON a.surveyID = b.surveyID
INNER JOIN Question c
ON b.questionID = c.questionID
GROUP BY a.surveyID
Upvotes: 2