咱們在寫Sql語句的時候沒常常會遇到將查詢結果行轉列,列轉行的需求,拼接sql字符串,而後使用sp_executesql執行sql字符串是比較常規的一種作法。可是這樣作實現起來很是複雜,而在SqlServer2005中咱們有了PIVOT/UNPIVOT函數能夠快速實現行轉列和列轉行的操做。html
PIVOT函數,行轉列sql
PIVOT函數的格式以下函數
PIVOT(<聚合函數>([聚合列值]) FOR [行轉列前的列名] IN([行轉列後的列名1],[行轉列後的列名2],[行轉列後的列名3],.......[行轉列後的列名N]))
下面咱們來看一個例子有一張表名爲[ShoppingCart]有三列[Week],[TotalPrice],[GroupId],數據和表結構以下所示:spa
CREATE TABLE [dbo].[ShoppingCart]( [Week] [int] NOT NULL, [TotalPrice] [decimal](18, 0) NOT NULL, [GroupId] [int] NULL ) ON [PRIMARY] GO ALTER TABLE [dbo].[ShoppingCart] ADD DEFAULT ((0)) FOR [TotalPrice] GO INSERT [dbo].[ShoppingCart] ([Week], [TotalPrice], [GroupId]) VALUES (1, CAST(10 AS Decimal(18, 0)), 1) GO INSERT [dbo].[ShoppingCart] ([Week], [TotalPrice], [GroupId]) VALUES (2, CAST(20 AS Decimal(18, 0)), 1) GO INSERT [dbo].[ShoppingCart] ([Week], [TotalPrice], [GroupId]) VALUES (3, CAST(30 AS Decimal(18, 0)), 1) GO INSERT [dbo].[ShoppingCart] ([Week], [TotalPrice], [GroupId]) VALUES (4, CAST(40 AS Decimal(18, 0)), 1) GO INSERT [dbo].[ShoppingCart] ([Week], [TotalPrice], [GroupId]) VALUES (5, CAST(50 AS Decimal(18, 0)), 1) GO INSERT [dbo].[ShoppingCart] ([Week], [TotalPrice], [GroupId]) VALUES (6, CAST(60 AS Decimal(18, 0)), 1) GO INSERT [dbo].[ShoppingCart] ([Week], [TotalPrice], [GroupId]) VALUES (7, CAST(70 AS Decimal(18, 0)), 1) GO
如今咱們是用PIVOT函數將列[WEEK]的行值轉換爲列,並使用聚合函數Count(TotalPrice)來統計每個Week列在轉換前有多少行數據,語句以下所示:3d
select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T
查詢結果以下:code
咱們能夠看到PIVOT函數成功地將[ShoppingCart]表列[Week]的行值轉換爲了七列,而且每一列統計轉換前的行數爲1,這符合咱們的預期結果。那麼根據咱們前面定義的PIVOT函數轉換格式,在本例中咱們有以下公式對應值:htm
另外若是咱們在[行轉列後的列名]中只聲明瞭部分值,那麼PIVOT函數只會針對這些部分值作行轉列,而那些沒有被聲明爲列的行值會在行轉列後被忽略掉。例如咱們下面的語句聲明瞭只對表ShoppingCart中[Week]列的1,2,3三個值作行轉列,可是實際上表ShoppingCart中列[Week]有1,2,3,4,5,6,7這7個值,那麼剩下的4到7就會被PIVOT函數忽略掉,以下所示:blog
select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3])) AS T
咱們能夠看到查詢結果中PIVOT函數只針對表ShoppingCart中列[Week]的1,2,3三個值作了行轉列,而4到7被忽略了。ci
須要注意的是PIVOT函數的查詢結果中多了一列GroupId,這是由於PIVOT函數只用到了[ShoppingCart]表中的列[Week]和[TotalPrice],[ShoppingCart]表中還有一列[GroupId],PIVOT函數沒有用到,因此PIVOT函數默認將[ShoppingCart]表中沒有用到的列當作了Group By來處理,用來做爲行轉列後每一行數據分行的依據,又因爲列[GroupId]在[ShoppingCart]表中全爲值1,因此最後PIVOT函數在行轉列後只有一行[GroupId]爲1的數據,若是咱們將[ShoppingCart]表列[GroupId]的值從只有1變成有1和2兩種值,以下所示:字符串
而後再執行PIVOT查詢:
select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T
會獲得以下結果:
咱們看到這一次咱們用PIVOT函數作行轉列後獲得了兩行值,能夠看到轉換後列[3]和[4]在[GroupId]爲2的這一行上爲1,這就是由於咱們將[ShoppingCart]表中[Week]爲3和4的兩行改爲了[GroupId]爲2後,[GroupId]有了兩個值1和2,因此PIVOT函數行轉列後就有兩行值。
知道了PIVOT函數的用法以後,咱們來看看PIVOT函數的幾種錯誤用法:
在PIVOT函數的使用中有一點須要注意,那就是[行轉列後的列名]必須是[行轉列前的列名]的值,PIVOT函數才能成功執行,好比以下所示若是咱們將[行轉列後的列名]聲明瞭一個和[行轉列前的列名]值絕不相干的數字1000,那麼PIVOT函數執行後1000是沒有任何數據的爲0:
select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7],[1000])) AS T
這是由於[ShoppingCart]表中列[Week]沒有值1000,因此用PIVOT函數將列[Week]行轉列後列[1000]的值就爲0。
若是將PIVOT函數中[行轉列後的列名]聲明爲了[行轉列前的列名]徹底不一樣的數據類型,還會致使PIVOT函數報錯,例以下面咱們在[行轉列後的列名]中聲明瞭一個列名爲字符串[TestColumnName],可是因爲[行轉列前的列名]Week是Int類型,從而沒法將字符串TestColumnName轉換爲Int類型,因此PIVOT函數報錯了:
select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7],[TestColumnName])) AS T
UNPIVOT函數,列轉行
UNPIVOT函數的格式以下:
UNPIVOT([轉換爲行的列值在轉換後對應的列名] for [轉換爲行的列名在轉換後對應的列名] in ([轉換爲行的列1],[轉換爲行的列2],[轉換爲行的列3],...[轉換爲行的列N]))
以下所示,列轉行前爲:
select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T
如今使用UNPIVOT函數將上面結果的列[1],[2],[3],[4],[5],[6],[7]轉換爲行值,以下所示:
with PIVOT_Table as ( select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T ) select * from PIVOT_Table UNPIVOT([RowCount] for [Week] in ([1],[2],[3],[4],[5],[6],[7])) as T
、
能夠看到[1],[2],[3],[4],[5],[6],[7]這七列在UNPIVOT函數執行後其值變爲了列[RowCount],列轉行前的列名稱在轉換後變爲了列[Week],一樣套用UNPIVOT函數格式咱們能夠獲得以下結果:
須要注意若是列轉行前有兩行值:
select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T
那麼UNPIVOT函數轉換後應該爲14行(列轉行前的行數2 X 須要進行列轉行的列數7 = 14)數據:
with PIVOT_Table as ( select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T ) select * from PIVOT_Table UNPIVOT([RowCount] for [Week] in ([1],[2],[3],[4],[5],[6],[7])) as T
此外須要注意UNPIVOT函數不會對列轉行中沒有用到的列做Group By處理,也不會對列傳行後的值作聚合運算,這一點是和PIVOT函數不一樣的。好比如今若是咱們有下面一個查詢:
with PIVOT_Table as ( select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T union all select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T ) select * from PIVOT_Table
起查詢結果爲:
咱們能夠看到查詢結果中有兩行GroupId爲1的數據,如今咱們再用UNPIVOT函數對這個查詢的列[1]到[7]作列轉行運算,其中沒有用到列GroupId:
with PIVOT_Table as ( select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T union all select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T ) select * from PIVOT_Table UNPIVOT([RowCount] for [Week] in ([1],[2],[3],[4],[5],[6],[7])) as T
結果以下所示:
咱們能夠看到結果出現了14行數據(列轉行前的行數2 X 須要進行列轉行的列數7 = 14),因此咱們能夠看到雖然咱們在UNPIVOT函數中沒有用到列GroupId,而且在列轉行前GroupId列有兩行相同的值1,可是UNPIVOT函數在列轉行後仍然生成了14行數據,而不是7行數據,所以並無對GroupId列作Group By處理來合併相同的值,這一點和前面的PIVOT函數是不一樣的。