将多个列拆分为多个行

San*_*dro 12 sql sql-server unpivot

我有一个这种结构的表.

UserID  | UserName  | AnswerToQuestion1 | AnswerToQuestion2 | AnswerToQuestion3
1       | John      | 1                 | 0                 | 1
2       | Mary      | 1                 | 1                 | 0
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚我将使用什么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
Run Code Online (Sandbox Code Playgroud)

我正在尝试将三列分成三个单独的行.这可能吗?

Eri*_*ikE 7

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)
Run Code Online (Sandbox Code Playgroud)

这对UNPIVOT(有时更好)也同样有效,并且也适用于SQL 2000.

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

请注意,如果您的问题清单长或者问题名称较长,则可能是2列X平台,一个问题号和一个用于问题名称实验.或者,如果您已经有一个包含问题列表的表格,那么CROSS就会加入.如果某些问题是NULL,那么最简单的方法是将上述查询放在CTE或派生表中,然后添加WHERE Response IS NOT NULL.


8kb*_*8kb 6

根据Itzik Ben-Gan在Inside Microsoft SQL Server 2008:T-SQL查询中,SQL Server在解开表格时会经历三个步骤:

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

第1步:生成副本

将创建一个虚拟表,其中包含orignal表中每行未被分配的每行的副本.此外,列名的字符串存储在新列中(将其称为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
Run Code Online (Sandbox Code Playgroud)

第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
Run Code Online (Sandbox Code Playgroud)

第3步:使用NULLS删除行

此步骤筛选出在"响应"列中使用空值创建的所有行.换句话说,如果任何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
Run Code Online (Sandbox Code Playgroud)

如果您按照这些步骤操作,则可以

  1. CROSS根据每个AnswerToQuestion列名称连接表中的所有行以获取行副本
  2. 根据与源列和QuestionName匹配的内容填充"响应"列
  3. 删除NULL以在不使用UNPIVOT的情况下获得相同的结果.

以下示例:

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
Run Code Online (Sandbox Code Playgroud)


Mar*_*ith 5

假设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;
Run Code Online (Sandbox Code Playgroud)