一.本文所涉及的内容(Contents)
二.背景(Contexts)
其实行转列并不是一个什么新鲜的话题了,甚至已经被大家说到烂了,网上的很多例子多多少少都有些问题,所以我希望能让大家快速的看到执行的效果,所以在动态列的基础上再把表、分组字段、行转列字段、值这四个行转列固定需要的值变成真正意义的参数化,大家只需要根据自己的环境,设置参数值,马上就能看到效果了(可以直接跳转至:“ 参数化动态PIVOT行转列 ”查看具体的脚本代码)。行转列的效果图如图1所示:
(图1:行转列效果图)
三.实现代码(SQL Codes)
(一) 首先我们先创建一个测试表,往里面插入测试数据,返回表记录如图2所示:
--
创建测试表
IF
EXISTS
(
SELECT
*
FROM
sys.objects
WHERE
object_id
=
OBJECT_ID
(N
'
[dbo].[TestRows2Columns]
'
)
AND
type
in
(N
'
U
'
))
DROP
TABLE
[
dbo
]
.
[
TestRows2Columns
]
GO
CREATE
TABLE
[
dbo
]
.
[
TestRows2Columns
]
(
[
Id
]
[
int
]
IDENTITY
(
1
,
1
)
NOT
NULL
,
[
UserName
]
[
nvarchar
]
(
50
)
NULL
,
[
Subject
]
[
nvarchar
]
(
50
)
NULL
,
[
Source
]
[
numeric
]
(
18
,
0
)
NULL
)
ON
[
PRIMARY
]
GO
--
插入测试数据
INSERT
INTO
[
TestRows2Columns
]
(
[
UserName
]
,
[
Subject
]
,
[
Source
]
)
SELECT
N
'
张三
'
,N
'
语文
'
,
60
UNION
ALL
SELECT
N
'
李四
'
,N
'
数学
'
,
70
UNION
ALL
SELECT
N
'
王五
'
,N
'
英语
'
,
80
UNION
ALL
SELECT
N
'
王五
'
,N
'
数学
'
,
75
UNION
ALL
SELECT
N
'
王五
'
,N
'
语文
'
,
57
UNION
ALL
SELECT
N
'
李四
'
,N
'
语文
'
,
80
UNION
ALL
SELECT
N
'
张三
'
,N
'
英语
'
,
100
GO
SELECT
*
FROM
[
TestRows2Columns
]
(图2:样本数据)
--
1:静态拼接行转列
SELECT
[
UserName
]
,
SUM
(
CASE
[
Subject
]
WHEN
'
数学
'
THEN
[
Source
]
ELSE
0
END
)
AS
'
[数学]
'
,
SUM
(
CASE
[
Subject
]
WHEN
'
英语
'
THEN
[
Source
]
ELSE
0
END
)
AS
'
[英语]
'
,
SUM
(
CASE
[
Subject
]
WHEN
'
语文
'
THEN
[
Source
]
ELSE
0
END
)
AS
'
[语文]
'
FROM
[
TestRows2Columns
]
GROUP
BY
[
UserName
]
GO
(图3:样本数据)
(三) 接着以动态的方式实现行转列,这是使用拼接SQL的方式实现的,所以它适用于SQL Server 2000以上的数据库版本,执行脚本返回的结果如图2所示;
--
2:动态拼接行转列
DECLARE
@sql
VARCHAR
(
8000
)
SET
@sql
=
'
SELECT [UserName],
'
SELECT
@sql
=
@sql
+
'
SUM(CASE [Subject] WHEN
'''
+
[
Subject
]
+
'''
THEN [Source] ELSE 0 END) AS
'''
+
QUOTENAME
(
[
Subject
]
)
+
'''
,
'
FROM
(
SELECT
DISTINCT
[
Subject
]
FROM
[
TestRows2Columns
]
)
AS
a
SELECT
@sql
=
LEFT
(
@sql
,
LEN
(
@sql
)
-
1
)
+
'
FROM [TestRows2Columns] GROUP BY [UserName]
'
PRINT
(
@sql
)
EXEC
(
@sql
)
GO
(四) 在SQL Server 2005之后有了一个专门的PIVOT 和 UNPIVOT 关系运算符做行列之间的转换,下面是静态的方式实现的,实现效果如图4所示:
--
3:静态PIVOT行转列
SELECT
*
FROM
(
SELECT
[
UserName
]
,
[
Subject
]
,
[
Source
]
FROM
[
TestRows2Columns
]
) p PIVOT
(
SUM
(
[
Source
]
)
FOR
[
Subject
]
IN
(
[
数学
]
,
[
英语
]
,
[
语文
]
) )
AS
pvt
ORDER
BY
pvt.
[
UserName
]
;
GO
(图4)
(五) 把上面静态的SQL基础上进行修改,这样就不用理会记录里面存储了什么,需要转成什么列名的问题了,脚本如下,效果如图4所示:
--
4:动态PIVOT行转列
DECLARE
@sql_str
VARCHAR
(
8000
)
DECLARE
@sql_col
VARCHAR
(
8000
)
SELECT
@sql_col
=
ISNULL
(
@sql_col
+
'
,
'
,
''
)
+
QUOTENAME
(
[
Subject
]
)
FROM
[
TestRows2Columns
]
GROUP
BY
[
Subject
]
SET
@sql_str
=
'
SELECT * FROM (
SELECT [UserName],[Subject],[Source] FROM [TestRows2Columns]) p PIVOT
(SUM([Source]) FOR [Subject] IN (
'
+
@sql_col
+
'
) ) AS pvt
ORDER BY pvt.[UserName]
'
PRINT
(
@sql_str
)
EXEC
(
@sql_str
)
(六) 也许很多人到了上面一步就够了,但是你会发现,当别人拿到你的代码,需要不断的修改成他自己环境中表名、分组列、行转列字段、字段值这几个参数,逻辑如图5所示,所以,我继续对上面的脚本进行修改,你只要设置自己的参数就可以实现行转列了,效果如图4所示:
--
5:参数化动态PIVOT行转列
--
=============================================
--
Author: <听风吹雨>
--
Create date: <2014.05.26>
--
Description: <参数化动态PIVOT行转列>
--
Blog: <http://www.cnblogs.com/gaizai/>
--
=============================================
DECLARE
@sql_str
NVARCHAR
(
MAX
)
DECLARE
@sql_col
NVARCHAR
(
MAX
)
DECLARE
@tableName
SYSNAME
--
行转列表
DECLARE
@groupColumn
SYSNAME
--
分组字段
DECLARE
@row2column
SYSNAME
--
行变列的字段
DECLARE
@row2columnValue
SYSNAME
--
行变列值的字段
SET
@tableName
=
'
TestRows2Columns
'
SET
@groupColumn
=
'
UserName
'
SET
@row2column
=
'
Subject
'
SET
@row2columnValue
=
'
Source
'
--
从行数据中获取可能存在的列
SET
@sql_str
=
N
'
SELECT @sql_col_out = ISNULL(@sql_col_out +
''
,
''
,
''''
) + QUOTENAME([
'
+
@row2column
+
'
])
FROM [
'
+
@tableName
+
'
] GROUP BY [
'
+
@row2column
+
'
]
'
--
PRINT @sql_str
EXEC
sp_executesql
@sql_str
,N
'
@sql_col_out NVARCHAR(MAX) OUTPUT
'
,
@sql_col_out
=
@sql_col
OUTPUT
--
PRINT @sql_col
SET
@sql_str
=
N
'
SELECT * FROM (
SELECT [
'
+
@groupColumn
+
'
],[
'
+
@row2column
+
'
],[
'
+
@row2columnValue
+
'
] FROM [
'
+
@tableName
+
'
]) p PIVOT
(SUM([
'
+
@row2columnValue
+
'
]) FOR [
'
+
@row2column
+
'
] IN (
'
+
@sql_col
+
'
) ) AS pvt
ORDER BY pvt.[
'
+
@groupColumn
+
'
]
'
--
PRINT (@sql_str)
EXEC
(
@sql_str
)
(图5)
(七) 在实际的运用中,我经常遇到需要对基础表的数据进行筛选后再进行行转列,那么下面的脚本将满足你这个需求,效果如图6所示:
--
6:带条件查询的参数化动态PIVOT行转列
--
=============================================
--
Author: <听风吹雨>
--
Create date: <2014.05.26>
--
Description: <参数化动态PIVOT行转列,带条件查询的参数化动态PIVOT行转列>
--
Blog: <http://www.cnblogs.com/gaizai/>
--
=============================================
DECLARE
@sql_str
NVARCHAR
(
MAX
)
DECLARE
@sql_col
NVARCHAR
(
MAX
)
DECLARE
@sql_where
NVARCHAR
(
MAX
)
DECLARE
@tableName
SYSNAME
--
行转列表
DECLARE
@groupColumn
SYSNAME
--
分组字段
DECLARE
@row2column
SYSNAME
--
行变列的字段
DECLARE
@row2columnValue
SYSNAME
--
行变列值的字段
SET
@tableName
=
'
TestRows2Columns
'
SET
@groupColumn
=
'
UserName
'
SET
@row2column
=
'
Subject
'
SET
@row2columnValue
=
'
Source
'
SET
@sql_where
=
'
WHERE UserName =
''
王五
'''
--
从行数据中获取可能存在的列
SET
@sql_str
=
N
'
SELECT @sql_col_out = ISNULL(@sql_col_out +
''
,
''
,
''''
) + QUOTENAME([
'
+
@row2column
+
'
])
FROM [
'
+
@tableName
+
'
]
'
+
@sql_where
+
'
GROUP BY [
'
+
@row2column
+
'
]
'
--
PRINT @sql_str
EXEC
sp_executesql
@sql_str
,N
'
@sql_col_out NVARCHAR(MAX) OUTPUT
'
,
@sql_col_out
=
@sql_col
OUTPUT
--
PRINT @sql_col
SET
@sql_str
=
N
'
SELECT * FROM (
SELECT [
'
+
@groupColumn
+
'
],[
'
+
@row2column
+
'
],[
'
+
@row2columnValue
+
'
] FROM [
'
+
@tableName
+
'
]
'
+
@sql_where
+
'
) p PIVOT
(SUM([
'
+
@row2columnValue
+
'
]) FOR [
'
+
@row2column
+
'
] IN (
'
+
@sql_col
+
'
) ) AS pvt
ORDER BY pvt.[
'
+
@groupColumn
+
'
]
'
--
PRINT (@sql_str)
EXEC
(
@sql_str
)
(图6)
四.参考文献(References)

