如何在没有聚合的情况下将行值动态地转换为列?

时间:2013-07-17 14:50:09

标签: sql tsql sql-server-2005 pivot

假设我有下表代表用户调查系统的结果。

SurveyID    ResponseID  QuestionID  Answer
 --------   ----------  ----------  ------
1           1           1           'Answer 1'
1           1           2           'Answer 2'
1           1           3           'Answer 3'
1           2           1           'red'
1           2           2           'blue'
1           2           3           'green'

我想要的是一个旋转输出,如下所示。

SurveyID    ResponseID  Q1          Q2          Q3
--------    ----------  --          --          --
1           1           'Answer 1'  'Answer 2'  'Answer 3'
1           2           'red'       'blue'      'green'

如果总是只有相同的三个问题,我知道如何实现这个目标,但是这个数据库包含多个调查,这些调查可能有任意数量的唯一QuestionID,所以我需要Q1,Q2,Q3列是动态的,具体取决于数量和该调查问题的ID。

我认为这是一个相当标准的问题,但我找不到任何完全满足这个问题的东西。任何解决方案都必须与SQL Server 2005一起使用。

希望这是有道理的。感谢。

2 个答案:

答案 0 :(得分:0)

1)枢纽需要聚合。您可能事先知道您只对一行感兴趣,但SQL不知道这一点。如果您每组只处理一行,只需使用MIN()作为汇总。

2)动态数据透视不是SQL的标准问题。这是表示层的任务,而不是数据层。您将不得不使用动态SQL,它仍然无法处理任意数量的列,如果您不小心,将会打开注入攻击。

如果你仍然想这样做:

CREATE TABLE #t (Surveyid int, Responseid int, Questionid int, Answer varchar(max))
INSERT #t VALUES (1,1,1,'Answer1'),(1,1,2,'Answer2'),(1,1,3,'Answer3'),(1,2,1,'red'),(1,2,2,'blue'),(1,2,3,'green')

DECLARE @qids nvarchar(4000)

SELECT @qids = COALESCE(@qids+',','') + qid
FROM (SELECT DISTINCT QUOTENAME(Questionid) qid FROM #t) t

EXEC ('SELECT [SurveyID],[ResponseID],'+@qids+' FROM #t PIVOT(MIN(Answer) FOR Questionid IN('+@qids+')) p')

答案 1 :(得分:0)

好的,终于发现了如何做到这一点,以为我会分享。

DECLARE @SurveyID SMALLINT;
DECLARE @SQL as VARCHAR(MAX);
DECLARE @Columns AS VARCHAR(MAX);
DECLARE @ColumnHeadings AS Varchar(MAX);

SET @SurveyID = 1;

SELECT
    @Columns = COALESCE(@Columns + ', ','') + '[' + QuestionID + ']'
    ,@ColumnHeadings = COALESCE(@ColumnHeadings + ', ','') + '[' + QuestionID + '] AS [Q' + QuestionNumber + ']'
FROM
(
    SELECT DISTINCT
        CAST(a.QuestionID AS VARCHAR) AS QuestionID
        ,CASE WHEN q.QuestionNumber IS NULL THEN '' ELSE q.QuestionNumber END AS QuestionNumber
    FROM dbo.Answers AS a
    JOIN dbo.Questions AS q
        ON a.QuestionID = q.ID
    JOIN dbo.SurveyResponses AS r
        ON a.ResponseID = r.ID
    WHERE r.SurveyID = @SurveyID
)

SET @SQL = '
WITH PivotData AS
(
    SELECT
        a.QuestionID
        ,a.ResponseID
        ,a.Answer
    FROM dbo.Answers AS a
    JOIN dbo.SurveyResponses AS r
        ON a.ResponseID = r.ID
)
SELECT 
    ResponseID
,' + @ColumnHeadings + '
FROM PivotData
PIVOT
(
    MAX(Answer)
    FOR QuestionID
    IN (' + @Columns + ')
) AS PivotResult
ORDER BY ResponseID' ASC

EXEC (@SQL);