根据 Inside Microsoft SQL Server 2008: T-SQL Querying 中的 Itzik Ben-Gan 所述,SQL Server 在取消透视表时会执行三个步骤:
- 生成副本
- 提取元素
- 删除带有 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
如果你按照这些步骤,你可以
- 针对每个 AnswerToQuestion 交叉连接表中的所有行
列名以获取行副本
- 填充基于响应列
在匹配源列和 QuestionName
- 删除 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