格式化从动态数据透视中获得的锯齿状数据

Format Jagged data gained from dynamic pivot

我需要格式化并从数据库中提取一些数据。虽然我可以成功提取数据,但我正在努力应对它的参差不齐的特性。

我有以下内容:

create table temp
(
    QuestionID INT,
    AnswerID INT,
    AnswerValue NVARCHAR(50)
)

insert into temp values (1, 1, 'Ans C')
insert into temp values (1, 2, 'Ans B')
insert into temp values (1, 3, 'Ans A')
insert into temp values (2, 4, 'Ans D')
insert into temp values (2, 5, 'Ans E')

DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX);

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.AnswerID) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT QuestionID, ' + @cols + ' from 
            (
                select QuestionID
                    , AnswerValue
                    , AnswerID
                from temp
           ) x
            pivot 
            (
                max(AnswerValue)
                for AnswerID in (' + @cols + ')
            ) p '


execute(@query)

drop table temp

执行此生成

+------------+-------+-------+-------+-------+-------+
| QuestionID |   1   |   2   |   3   |   4   |   5   |
+------------+-------+-------+-------+-------+-------+
|          1 | Ans C | Ans B | Ans A | NULL  | NULL  |
|          2 | NULL  | NULL  | NULL  | Ans D | Ans E |
+------------+-------+-------+-------+-------+-------+

我只需要这样格式化就可以了

+------------+-------+-------+-------+
| QuestionID |  Q1   |  Q2   |  Q3   |
+------------+-------+-------+-------+
|          1 | Ans C | Ans B | Ans A |
|          2 | NULL  | Ans D | Ans E |
+------------+-------+-------+-------+

请注意,由于限制,这需要在 SQL 中完成,而不是像 c# 这样的高级语言。

代码有几处错误。首先,您正在使用 AnswerID 创建您的列列表,因此数据被拆分到多个列而不是每个问题的答案。

为了解决这个问题,您需要使用 row_number() 之类的窗口函数为每个 question/answer 组合创建一个序列。

创建动态列时,将代码更改为:

SET @cols = STUFF((SELECT ',' + QUOTENAME('Q'+cast(rn as varchar(10))) 
            FROM
            (
              SELECT rn = row_number() over(partition by QuestionID
                                             order by AnswerID) 
              FROM temp
            ) c
            group by rn
            order by rn
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

这将使用 row_number() 并将根据 QuestionID 创建列名。然后你将在你的子查询中包含 row_number() 使你的代码:

DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX)

SET @cols = STUFF((SELECT ',' + QUOTENAME('Q'+cast(rn as varchar(10))) 
            FROM
            (
              SELECT rn = row_number() over(partition by QuestionID
                                             order by AnswerID) 
              FROM temp
            ) c
            group by rn
            order by rn
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT QuestionID, ' + @cols + ' 
           from 
           (
                select QuestionID
                  , AnswerValue
                  , col = ''Q''+ cast(row_number() over(partition by QuestionID
                                                         order by AnswerID) as varchar(10))
                from temp
           ) x
            pivot 
            (
                max(AnswerValue)
                for col in (' + @cols + ')
            ) p '

exec sp_executesql @query;

参见SQL Fiddle with Demo.这给出了一个结果:

| QUESTIONID |    Q1 |    Q2 |     Q3 |
|------------|-------|-------|--------|
|          1 | Ans C | Ans B |  Ans A |
|          2 | Ans D | Ans E | (null) |

您可以使用这部分代码:

SELECT 'A' + CAST(ROW_NUMBER() OVER(PARTITION BY QuestionID ORDER BY Answer) AS VARCHAR(10)) AS cName
FROM tblAnswers

以便生成所需的列名。上面给你的东西像:

cName
-----
A1
A2
A3
A1
A2

您随后可以在动态数据透视表中使用上述内容以获得所需的结果:

DECLARE @cols AS NVARCHAR(MAX), @query  AS NVARCHAR(MAX);

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(a.cName) 
                   FROM (
                      SELECT 'A' + CAST(ROW_NUMBER() OVER(PARTITION BY QuestionID ORDER BY Answer) AS VARCHAR(10)) AS cName
                      FROM tblAnswers
                   ) a
                   FOR XML PATH(''), TYPE
                  ).value('.', 'NVARCHAR(MAX)'),1,1,'')

set @query = 'SELECT Question, ' + @cols + ' ' +
             'FROM (
                SELECT q.Question, a.Answer,
                       ''A'' + CAST(ROW_NUMBER() OVER(PARTITION BY a.QuestionID ORDER BY Answer) AS VARCHAR(10)) AS cName
                FROM tblAnswers AS a
                INNER JOIN tblQuestions AS q ON a.QuestionID = q.QuestionID 
              ) t 
              PIVOT
              (
                 MAX(t.Answer)
                 FOR cName in (' + @cols + ')
              ) Pvt '

execute(@query)

上面的输出如下:

Question    A1      A2       A3
-----------------------------------
Q1          Answer1 Answer2 Answer3
Q2          Answer4 Answer5 NULL

SQL Fiddle demo here