15

我有一张这种结构的桌子。

UserID  | UserName  | AnswerToQuestion1 | AnswerToQuestion2 | AnswerToQuestion3
1       | John      | 1                 | 0                 | 1
2       | Mary      | 1                 | 1                 | 0

我不知道我会使用什么 SQL 查询来获得这样的结果集:

UserID  | UserName  | QuestionName      | Response
1       | John      | AnswerToQuestion1 | 1
1       | John      | AnswerToQuestion2 | 0
1       | John      | AnswerToQuestion3 | 1
2       | Mary      | AnswerToQuestion1 | 1
2       | Mary      | AnswerToQuestion2 | 1
2       | Mary      | AnswerToQuestion3 | 0

我试图将三列分成三个单独的行。这可能吗?

4

3 回答 3

8
SELECT
   Y.UserID,
   Y.UserName,
   QuestionName = 'AnswerToQuestion' + X.Which,
   Response =
      CASE X.Which
      WHEN '1' THEN AnswerToQuestion1
      WHEN '2' THEN AnswerToQuestion2
      WHEN '3' THEN AnswerToQuestion3
      END
FROM
   YourTable Y
   CROSS JOIN (SELECT '1' UNION ALL SELECT '2' UNION ALL SELECT '3') X (Which)

这与 UNPIVOT 一样好(有时更好)并且在 SQL 2000 中也可以工作。

我利用问题的相似性来创建 QuestionName 列,但当然这适用于不同的问题名称。

请注意,如果您的问题列表很长或问题名称很长,您可以尝试在 X 表中使用 2 列,一列用于问题编号,另一列用于问题名称。或者,如果您已经有一个包含问题列表的表格,那么 CROSS JOIN 到那个表格。如果某些问题为 NULL,那么最简单的方法是将上述查询放入 CTE 或派生表中,然后添加WHERE Response IS NOT NULL.

于 2010-09-10T04:38:34.457 回答
6

根据 Itzik Ben-Gan 在Inside Microsoft SQL Server 2008: T-SQL Querying 中的说法,SQL Server 在取消透视表时会经历三个步骤:

  1. 生成副本
  2. 提取元素
  3. 删除带有 NULL 的行

第 1 步:生成副本

将创建一个虚拟表,其中包含原始表中每一行的副本,其中每一列都将被取消透视。此外,列名的字符串存储在新列中(称为 QuestionName 列)。*注意:我将您的一列中的值修改为 NULL 以显示完整过程。

UserID  UserName  AnswerTo1 AnswerToQ2 AnswerToQ3 QuestionName
1       John      1         0          1          AnswerToQuestion1
1       John      1         0          1          AnswerToQuestion2
1       John      1         0          1          AnswerToQuestion3
2       Mary      1         NULL       1          AnswerToQuestion1
2       Mary      1         NULL       1          AnswerToQuestion2
2       Mary      1         NULL       1          AnswerToQuestion3

第 2 步:提取元素

然后创建另一个表,该表为源列中的每个值创建一个新行,该行对应于 QuestionName 列中的字符串值。该值存储在一个新列中(称为响应列)。

UserID  UserName  QuestionName        Response
1       John      AnswerToQuestion1   1
1       John      AnswerToQuestion2   0
1       John      AnswerToQuestion3   1
2       Mary      AnswerToQuestion1   1
2       Mary      AnswerToQuestion2   NULL
2       Mary      AnswerToQuestion3   1

第 3 步:删除带有 NULLS 的行

此步骤过滤掉在 Response 列中使用空值创建的任何行。换句话说,如果任何 AnswerToQuestion 列有一个空值,它就不会被表示为一个未透视的行。

UserID  UserName  QuestionName        Response
1       John      AnswerToQuestion1   1
1       John      AnswerToQuestion2   0
1       John      AnswerToQuestion3   1
2       Mary      AnswerToQuestion1   1
2       Mary      AnswerToQuestion3   1

如果你按照这些步骤,你可以

  1. 针对每个 AnswerToQuestion 列名 CROSS JOIN 表中的所有行以获取行副本
  2. 根据匹配的源列和 QuestionName 填充 Response 列
  3. 在不使用 UNPIVOT 的情况下删除 NULL 以获得相同的结果。

下面的一个例子:

DECLARE @t1 TABLE (UserID INT, UserName VARCHAR(10), AnswerToQuestion1 INT, 
  AnswertoQuestion2 INT, AnswerToQuestion3 INT
) 

INSERT @t1 SELECT 1, 'John', 1, 0, 1 UNION ALL SELECT 2, 'Mary', 1, NULL, 1 

SELECT
  UserID,
  UserName,
  QuestionName,
  Response
FROM (
  SELECT
    UserID,
    UserName,
    QuestionName,
    CASE QuestionName
      WHEN 'AnswerToQuestion1' THEN AnswerToQuestion1
      WHEN 'AnswerToQuestion2' THEN AnswertoQuestion2
      ELSE AnswerToQuestion3 
    END AS Response 
  FROM @t1 t1
      CROSS JOIN (
        SELECT 'AnswerToQuestion1' AS QuestionName
        UNION ALL SELECT 'AnswerToQuestion2'
        UNION ALL SELECT 'AnswerToQuestion3'
      ) t2
    ) t3
WHERE Response IS NOT NULL
于 2010-09-10T05:57:54.860 回答
6

假设 SQL Server 2005+ 你可以使用UNPIVOT

;with YourTable as
(
SELECT 1 UserID,'John' UserName,1 AnswerToQuestion1,0 AnswerToQuestion2,1 AnswerToQuestion3 
UNION ALL
SELECT 2, 'Mary', 1, 1, 0
)
SELECT UserID, UserName, QuestionName, Response
FROM YourTable
UNPIVOT
   (Response FOR QuestionName IN 
      (AnswerToQuestion1, AnswerToQuestion2,AnswerToQuestion3)
)AS unpvt;
于 2010-09-09T16:28:05.163 回答