2013-10-22 88 views
13

我在一個數據庫中有一個表,它有9列包含相同類型的數據,這些值是允許爲空。我需要將每個非空值選擇到不關心它們所源自的行的標識的單個值列中。從多列中選擇值到單列

因此,對於一個表,看起來像這樣:

+---------+------+--------+------+ 
| Id | I1 | I2  | I3 | 
+---------+------+--------+------+ 
| 1 | x1 | x2  | x7 | 
| 2 | x3 | null | x8 | 
| 3 | null | null | null| 
| 4 | x4 | x5  | null| 
| 5 | null | x6  | x9 | 
+---------+------+--------+------+ 

我希望每個X爲前綴的值的選擇到一個列。我的結果數據應該如下表所示。訂單需要保留的,所以從第一行第一列的值應該是在頂部,並從最後一排在底部的最後一列值:

+-------+ 
| value | 
+-------+ 
| x1 | 
| x2 | 
| x7 | 
| x3 | 
| x8 | 
| x4 | 
| x5 | 
| x6 | 
| x9 | 
+-------+ 

我使用SQL Server 2008 R2的 。有沒有更好的技術來實現這一點,而不是從每一行依次選擇每列的值,並將非空值插入結果中?

回答

17

可以使用UNPIVOT函數來得到最終結果:

select value 
from yourtable 
unpivot 
(
    value 
    for col in (I1, I2, I3) 
) un 
order by id, col; 

由於您使用的SQL Server 2008+,那麼你就可以還可以使用帶VALUES子句的CROSS APPLY來反轉色譜柱:

select value 
from yourtable 
cross apply 
(
    values 
     ('I1', I1), 
     ('I2', I2), 
     ('I3', I3) 
) c(col, value) 
where value is not null 
order by id, col 
+8

這裏需要注意的重要一點是,與[當前最高票數的答案](http://stackoverflow.com/a/19515927/61305)不同,這會使發生的全表掃描次數最少(1次vs. 3次)。如果你有5列,或者8列,或者22列,這兩者都會增加掃描次數到這個數字,並且還會使查詢的UNION版本更難以編寫/查看。 –

5
SELECT value FROM (
    SELECT ID, 1 AS col, I1 AS [value] FROM t 
    UNION ALL SELECT ID, 2, I2 FROM t 
    UNION ALL SELECT ID, 3, I3 FROM t 
) AS t WHERE value IS NOT NULL ORDER BY ID, col; 
2

嘗試工會象下面這樣:

select value from 
(
    select col1 as value from TestTable 
    union 
    select col2 as value from TestTable 
    union 
    select col3 as value from TestTable 
) tt where value is not null