行列转换,通常有2种方法,一是CASE WHEN/UNION;一是PIVOT/UNPIVOT。对于行值或列数不固定的情况,需要用动态SQL。
一. 行转列
--
drop table RowToCol
create
table
RowToCol
(
ID
int
,
Code
varchar
(
10
),
Value
int
)
Go
insert
RowToCol
select
1
,
'
Item1
'
,
1000
union
all
select
1
,
'
Item2
'
,
1000
union
all
select
1
,
'
Item3
'
,
500
union
all
select
2
,
'
Item1
'
,
2000
union
all
select
2
,
'
Item2
'
,
0
union
all
select
3
,
'
Item1
'
,
1000
union
all
select
3
,
'
Item3
'
,
500
GO
select
*
from
RowToCol
要得到这样的结果:
| ID | Item1 | Item2 | Item3 |
| 1 | 1000 | 1000 | 500 |
| 2 | 2000 | 0 | 0 |
| 3 | 1000 | 0 | 500 |
1. CASE WHEN
在SQL Server 2000时,常用的写法,沿用至今。
(1) 静态
select
ID,
sum
(
case
Code
when
'
Item1
'
then
Value
else
0
end
)
as
Item1,
sum
(
case
Code
when
'
Item2
'
then
Value
else
0
end
)
as
Item2,
sum
(
case
Code
when
'
Item3
'
then
Value
else
0
end
)
as
Item3
from
RowToCol
group
by
ID
--
或者用max也行
select
ID,
max
(
case
Code
when
'
Item1
'
then
Value
else
0
end
)
as
Item1,
max
(
case
Code
when
'
Item2
'
then
Value
else
0
end
)
as
Item2,
max
(
case
Code
when
'
Item3
'
then
Value
else
0
end
)
as
Item3
from
RowToCol
group
by
ID
(2) 动态
在不确定有多少行需要转为列时,先distinct出待转列的值,再拼出包含若干个CASE的SQL语句,然后运行。
declare
@sql
varchar
(
8000
)
set
@sql
=
'
select ID
'
select
@sql
=
@sql
+
'
, max(case Code when
'''
+
Code
+
'''
then Value else 0 end) [
'
+
Code
+
'
]
'
from
(
select
distinct
Code
from
RowToCol)
as
a
set
@sql
=
@sql
+
'
from RowToCol group by ID
'
--
print @sql
exec
(
@sql
)
2. PIVOT
PIVOT是SQL Server 2005的新语法,Oracle在11g中也推出了这个语法。
(1) 静态
select
*
from
(
select
*
from
RowToCol) a
pivot (
max
(value)
for
Code
in
(
[
Item1
]
,
[
Item2
]
,
[
Item3
]
)) b
(2) 动态
用PIVOT拼写动态SQL时就简单了,只要把后面的列清单整理出来就可以了。
declare
@sql
varchar
(
8000
)
select
@sql
=
isnull
(
@sql
+
'
],[
'
,
''
)
+
Code
from
RowToCol
group
by
Code
set
@sql
=
'
[
'
+
@sql
+
'
]
'
--
print @sql
exec
(
'
select * from (select * from RowToCol) a pivot (max(value) for Code in (
'
+
@sql
+
'
)) b
'
)
二. 列转行
--
drop table ColToRow
create
table
ColToRow
(
ID
int
,
Item1
int
,
Item2
int
,
Item3
int
)
GO
insert
into
ColToRow
select
'
1
'
,
1000
,
1000
,
500
union
all
select
'
2
'
,
2000
,
0
,
0
union
all
select
'
3
'
,
1000
,
0
,
500
GO
select
*
from
ColToRow
要得到这样的结果:
| ID | Code | Value |
| 1 | Item1 | 1000 |
| 1 | Item2 | 1000 |
| 1 | Item3 | 500 |
| 2 | Item1 | 2000 |
| 2 | Item2 | 0 |
| 2 | Item3 | 0 |
| 3 | Item1 | 1000 |
| 3 | Item2 | 0 |
| 3 | Item3 | 500 |
1. UNION
在SQL Server 2000时,常用的写法,沿用至今。
(1) 静态
select
ID,Code
=
'
Item1
'
,Value
=
Item1
from
ColToRow
union
all
select
ID,Code
=
'
Item2
'
,Value
=
Item2
from
ColToRow
union
all
select
ID,Code
=
'
Item3
'
,Value
=
Item3
from
ColToRow
order
by
ID
SQL Server对于多个UNION的排序,只要在最后加ORDER BY就可以了。
(2) 动态
在不确定有多少列需要转为行时,先借助系统表syscolumns找出待转行的列,再拼出包含若干个UNION语句,然后运行。
declare
@sql
varchar
(
8000
)
select
@sql
=
isnull
(
@sql
+
'
union all
'
,
''
)
+
'
select ID , [Code] =
'
+
quotename
(Name ,
''''
)
+
'
, [Value] =
'
+
quotename
(Name)
+
'
from ColToRow
'
from
syscolumns
where
name
<>
N
'
ID
'
and
ID
=
object_id
(
'
ColToRow
'
)
order
by
colid
asc
--
print @sql
exec
(
@sql
+
'
order by ID
'
)
2. UNPIVOT
UNPIVOT是SQL Server 2005的新语法,Oracle在11g中也推出了这个语法。
(1) 静态
select
ID , Code , Value
from
ColToRow
unpivot (Value
for
Code
in
(
[
Item1
]
,
[
Item2
]
,
[
Item3
]
)) t
(2) 动态
declare
@sql
varchar
(
8000
)
select
@sql
=
isnull
(
@sql
+
'
],[
'
,
''
)
+
name
from
syscolumns
where
name
<>
N
'
ID
'
and
ID
=
object_id
(
'
ColToRow
'
)
set
@sql
=
'
[
'
+
@sql
+
'
]
'
--
print @sql
exec
(
'
select ID , Code , Value from ColToRow unpivot (Value for Code in(
'
+
@sql
+
'
)) t
'
)

