SqlServer PIVOT函數快速實現行轉列,UNPIVOT實現列轉行(轉)

咱們在寫Sql語句的時候沒常常會遇到將查詢結果行轉列,列轉行的需求,拼接sql字符串,而後使用sp_executesql執行sql字符串是比較常規的一種作法。可是這樣作實現起來很是複雜,而在SqlServer2005中咱們有了PIVOT/UNPIVOT函數能夠快速實現行轉列和列轉行的操做。html

 

PIVOT函數,行轉列sql

 

PIVOT函數的格式以下函數

PIVOT(<聚合函數>([聚合列值]) FOR [行轉列前的列名] IN([行轉列後的列名1],[行轉列後的列名2],[行轉列後的列名3],.......[行轉列後的列名N]))
  • <聚合函數>就是咱們使用的SUM,COUNT,AVG等Sql聚合函數,也就是行轉列後計算列的聚合方式。
  • [聚合列值]要進行聚合的列名
  • [行轉列前的列名]這個就是須要將行轉換爲列的列名。
  • [行轉列後的列名]這裏須要聲明將行的值轉換爲列後的列名,由於轉換後的列名其實就是轉換前行的值,因此上面格式中的[行轉列後的列名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

  • <聚合函數>本例中爲Count
  • [聚合列值]本例中爲[TotalPrice],統計了行轉列前的行數
  • [行轉列前的列名]本例中爲[Week]
  • [行轉列後的列名]本例中爲[1],[2],[3],[4],[5],[6],[7]七個列,由於行轉列前[ShoppingCart]表的[Week]列有七個值1,2,3,4,5,6,7,因此這裏聲明轉換後的列名也爲七個,對應這七個值分別爲[1],[2],[3],[4],[5],[6],[7],PIVOT函數會將[ShoppingCart]表中[Week]列的值分別和[1],[2],[3],[4],[5],[6],[7]這七列的列名進行匹配,而後計算<聚合函數>(本例中爲count(TotalPrice))得出轉換後的列值。

另外若是咱們在[行轉列後的列名]中只聲明瞭部分值,那麼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函數格式咱們能夠獲得以下結果:

  • [轉換爲行的列值在轉換後對應的列名]在本例中爲[RowCount]
  • [轉換爲行的列名在轉換後對應的列名]在本例中爲[Week]
  • [轉換爲行的列]這個是聲明哪些列要進行列轉行,在本例中爲[1],[2],[3],[4],[5],[6],[7]這七列

 

須要注意若是列轉行前有兩行值:

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函數是不一樣的。

轉載來源 http://www.javashuo.com/article/p-zyumdvxx-er.html

相關文章
相關標籤/搜索