2010-09-09 258 views
10

我有一個具有此結構的表。將多個列拆分爲多個行

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 

我想三列拆分爲三個獨立的行。這可能嗎?

回答

6
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

+0

我得到'列問題不存在'與此 – hedgedandlevered 2016-12-09 17:38:22

+0

不,我的查詢工作正常。只有在查詢中的其他位置嘗試引用別名'QuestionName'時纔會出現該錯誤。將這個查詢放入派生表中並從中選擇,然後可以在其上添加條件。你正在使用SQL Server,對嗎? – ErikE 2016-12-09 17:50:31

+0

哦,不,我正在使用常規的SQL。沒有看到sql-server標籤。 謝謝,最終找出了使用8kb的答案和你的。 – hedgedandlevered 2016-12-09 18:55:51

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; 
+0

很抱歉的響應晚,感謝您的答覆。有沒有其他方法可以使用UNPIVOT運營商? – Sandro 2010-09-09 19:01:38

6

根據伊茨克奔甘在Inside Microsoft SQL Server 2008: T-SQL Querying,unpivoting表時SQL服務器要經歷三個步驟:

  1. 生成副本
  2. 提取元素
  3. 與空值刪除行

步驟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 

步驟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:用null

這一步篩選出用在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. CROSS JOIN所有表中的行鍼對每個AnswerToQuestion 列名獲得行副本
  2. 填充的匹配基於 響應列源列和問題名稱
  3. 刪除空值以獲得與使用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 
+0

非常感謝您的質量響應。我真的很喜歡你的回答,因爲它是最詳細的,但我可能必須給Emtucifor回答幾個小時纔回答。謝謝! – Sandro 2010-09-10 14:01:16