选择匹配的行作为列

时间:2012-11-01 12:57:45

标签: sql sql-server pivot

我认为通过示例展示是最快的,所以这里是我的问题的简化版本。我正在使用SQL Server。

这是数据模型

SURVEY       QUESTION        ANSWER
------       --------        -------
surveyId     questionId      answerId
             questionText    surveyId
                             questionId
                             answerText

以下是一些示例数据

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          | john@aa.bb |
| 3        | 1        | 3          | New York   |
| 4        | 1        | 4          | 1112223344 |
| 5        | 2        | 1          | Pete       |
| 6        | 2        | 2          | pete@cc.dd |
| 7        | 2        | 3          | Boston     |
| 8        | 2        | 4          | 5556667788 |
 -----------------------------------------------

最后,这是我想要选择的内容

RESULT
 ------------------------------
| surveyId | name | email      |
 ------------------------------
| 1        | John | john@aa.bb |
| 2        | Pete | pete@dd.cc |
 ------------------------------

我希望能够在运行时指定我在最终表中需要哪些列。现在我可以通过以下方式获得此结果

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

正如您所看到的,它很难看,并且包含重复的代码。有没有办法做到这个清洁?

4 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

SQL Server有一个PIVOT函数可用于此类查询。如果您知道要转换的值,则可以对值进行硬编码:

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

请参阅SQL Fiddle with Demo

现在,如果你有一个未知数量的值,你可以使用动态sql来pivot数据:

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)

请参阅SQL Fiddle with Demo

答案 2 :(得分:2)

SQL FIDDLE EXAMPLE

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

答案 3 :(得分:1)

可能不是PIVOT的“正确”用法,但以下方法可以解决(请求SQL2005或更高版本):

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