我们在写Sql语句的时候没经常会遇到将查询结果行转列,列转行的需求,拼接sql字符串,然后使用执行sql字符串是比较常规的一种做法。但是这样做实现起来非常复杂,而在SqlServer2005中我们有了PIVOT函数可以快速实现行转列.
PIVOT函数,行转列
PIVOT函数的格式如下
PIVOT(([聚合列值]) FOR [行转列前的列名] IN([行转列后的列名1],[行转列后的列名2],[行转列后的列名3],.......[行转列后的列名N]))
- 就是我们使用的SUM,COUNT,AVG等Sql聚合函数,也就是行转列后计算列的聚合方式。
- [聚合列值]要进行聚合的列名
- [行转列前的列名]这个就是需要将行转换为列的列名。
- [行转列后的列名]这里需要声明将行的值转换为列后的列名,因为转换后的列名其实就是转换前行的值,所以上面格式中的[行转列后的列名1],[行转列后的列名2],[行转列后的列名3],......[行转列后的列名N]其实就是[行转列前的列名]每一行的值。
接下来我们造一个例子,生成一个表[shopping]有三列[week],[price],[groupId],数据和表结构如下图所示
CREATE TABLE [dbo].[Shopping]( [Week] [int] NOT NULL, [Price] [decimal](18, 0) NOT NULL, [GroupId] [int] NULL) ON [PRIMARY]GOALTER TABLE [dbo].[Shopping] ADD DEFAULT ((0)) FOR [Price]GOINSERT [dbo].[Shopping] ([Week], [Price], [GroupId]) VALUES (1, CAST(10 AS Decimal(18, 0)), 1)GOINSERT [dbo].[Shopping] ([Week], [Price], [GroupId]) VALUES (2, CAST(20 AS Decimal(18, 0)), 1)GOINSERT [dbo].[Shopping] ([Week], [Price], [GroupId]) VALUES (3, CAST(30 AS Decimal(18, 0)), 1)GOINSERT [dbo].[Shopping] ([Week], [Price], [GroupId]) VALUES (4, CAST(40 AS Decimal(18, 0)), 1)GOINSERT [dbo].[Shopping] ([Week], [Price], [GroupId]) VALUES (5, CAST(50 AS Decimal(18, 0)), 1)GOINSERT [dbo].[Shopping] ([Week], [Price], [GroupId]) VALUES (6, CAST(60 AS Decimal(18, 0)), 1)GOINSERT [dbo].[Shopping] ([Week], [Price], [GroupId]) VALUES (7, CAST(70 AS Decimal(18, 0)), 1)GO
查询表信息
select * from [Shopping]
查询结果
现在我们用PIVOT函数将列[WEEK]的行值转换为列,并使用聚合函数Count(TotalPrice)来统计每一个Week列在转换前有多少行数据,语句如下所示:
select *from Shopping as C PIVOT(SUM(Price) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T
可以看到:
1.PIVOT的格式:
SELECT [原表字段1,2,3…] FROM [表名]
AS [原表别名]
PIVOT( [聚合函数] ( [原表字段1] ) FOR [原表字段2] IN ( [原表2值1],[原表字段2值2]… ) ) AS [新表别名]
2.注意点:
(1)PIVOT必须列举[原表字段2的值],列举的值必须用中括号 [ ] 包含起来,就算是字符串类型也不需要单引号 ’ ’
(2)PIVOT中列举的值将作为新表的字段名称
(3)为什么会有聚合函数?此处并没有GROUP BY !GROUP BY 是隐藏的,除了语句中出现的两个 [原表字段],其他[原表字段]将被GROUP BY,这样才使得上面的PIVOT结果出现多行
(4)列举字段的这个组在原表中没有数据将以NULL值存在于PIVOT后的新表