34,838
社区成员




RT
一直见到很多零零散散的行列转换,列值合并拆分举例的帖子
论坛问这种类型的人也很多很多
没见到一个真正好好整理到一个帖子里的
希望有大神总结下,欢迎接分!
请按顺序如:
1.行转列
2.列转行
3.合并列值
4.拆分列值
--尽量美观,给同学们增加可读性!
create table tb(id int, value varchar(10))
insert into tb values(1, 'aa')
insert into tb values(1, 'bb')
insert into tb values(2, 'aaa')
insert into tb values(2, 'bbb')
insert into tb values(2, 'ccc')
go
--1
CREATE FUNCTION dbo.f_strUnite(@id int)
RETURNS varchar(8000)
AS
BEGIN
DECLARE @str varchar(8000)
SET @str = ''
SELECT @str = @str + ',' + value FROM tb WHERE id=@id
RETURN STUFF(@str, 1, 1, '')
END
GO
-- 调用函数
SELECt id, value = dbo.f_strUnite(id) FROM tb GROUP BY id
drop table tb
drop function dbo.f_strUnite
go
--2
SELECT * FROM(SELECT DISTINCT id FROM tb)A OUTER APPLY(
SELECT [values]= STUFF(REPLACE(REPLACE(
(
SELECT value FROM tb N
WHERE id = A.id
FOR XML AUTO
), ' <N value="', ','), '"/>', ''), 1, 1, '')
)N
go
--3
select id, [values]=stuff((select ','+[value] from tb t where id=tb.id for xml path('')), 1, 1, '')
from tb
group by id
----------------------------------------------
-------------------------------------------------
create table #tb(时间 datetime,状态 varchar(10), 名称 varchar(10)
)
truncate table #tb
insert into #tb
select '2011/8/2 23:36:18','1', 'A'
union all
select '2011/8/2 23:36:18','1', 'B'
union all
select '2011/8/2 23:36:18','2', 'A'
union all
select '2011/8/2 23:36:18','2', 'B'
union all
select '2011/8/2 23:36:18','3', 'A'
union all
select '2011/8/2 23:36:18','3', 'B'
union all
select '2011/8/3 23:36:18','1', 'A'
union all
select '2011/8/3 23:36:18','2', 'A'
union all
select '2011/8/3 23:36:18','3', 'A'
SELECT 名称,
max(CASE WHEN 状态='1' THEN 时间 ELSE '' END) as 状态1时间,
max(CASE WHEN 状态='2' THEN 时间 ELSE '' END) as 状态2时间,
max(CASE WHEN 状态='3' THEN 时间 ELSE '' END) as 状态3时间
from #tb
group by 名称
select * from #tb where 状态=1
select 名称,STUFF((select ','+convert(varchar(19),时间,120) from #tb where 状态=1 and 名称=a.名称 for xml path('')),1,1,'') as 状态1时间,
STUFF((select ','+convert(varchar(19),时间,120) from #tb where 状态=2 and 名称=a.名称 for xml path('')),1,1,'') as 状态2时间,
STUFF((select ','+convert(varchar(19),时间,120) from #tb where 状态=3 and 名称=a.名称 for xml path('')),1,1,'') as 状态3时间
from #tb a
group by 名称
create table xm
(
编号 int,
姓名 varchar(100)
)
insert into xm values(1,'甲;乙;丙;丁;戊')
insert into xm values(2,'乙;丙;丁')
insert into xm values(3,'甲;乙')
insert into xm values(4,'丙;丁')
insert into xm values(5,'乙;丙;戊')
insert into xm values(6,'甲')
insert into xm values(7,'乙')
insert into xm values(8,'丙')
insert into xm values(9,'丁;戊')
insert into xm values(11,'甲乙;乙')
SELECT B.姓名,A.编号
FROM(
SELECT 编号, 姓名 = CONVERT(xml,'<root><v>' + REPLACE(姓名, ';', '</v><v>') + '</v></root>') FROM xm
)A
OUTER APPLY(
SELECT 姓名 = N.v.value('.', 'varchar(100)') FROM A.姓名.nodes('/root/v') N(v)
)B
go
select 姓名=SUBSTRING(a.姓名,b.number,CHARINDEX(';',a.姓名+';',b.number)-b.number),a.编号
from xm a join master..spt_values b on b.type='p' and b.number between 1 and len(a.姓名)
where CHARINDEX(';',';'+a.姓名,b.number)=b.number
--------------------------------------------|
--------------------------------------------|
if not object_id('Tab') is null
drop table Tab
Go
Create table Tab([Col1] int,[COl2] nvarchar(5))
Insert Tab
select 1,N'a,b,c' union all
select 2,N'd,e' union all
select 3,N'f'
Go
update Tab set COl2='' where Col1=1
--SQL2000用辅助表:
select * from #num
if object_id('Tempdb..#Num') is not null
drop table #Num
go
select top 100 ID=Identity(int,1,1) into #Num from syscolumns a,syscolumns b
Select
a.Col1,COl2=substring(a.Col2,b.ID,charindex(',',a.Col2+',',b.ID)-b.ID)
from
Tab a,#Num b
where
charindex(',',','+a.Col2,b.ID)=b.ID --也可用 substring(','+a.COl2,b.ID,1)=','
--2000不使用辅助表
Select
a.Col1,COl2=substring(a.Col2,b.number,charindex(',',a.Col2+',',b.number)-b.number)
from
Tab a join master..spt_values b
ON B.type='p' AND B.number BETWEEN 1 AND LEN(A.col2)
where
substring(','+a.COl2,b.number,1)=','
SQL2005用Xml:
select
a.COl1,b.Col2
from
(select Col1,COl2=convert(xml,'<root><v>'+replace(COl2,',','</v><v>')+'</v></root>') from Tab)a
outer apply
(select Col2=C.v.value('.','nvarchar(100)') from a.COl2.nodes('/root/v')C(v))b
SQL05用CTE:
with roy as
(select Col1,COl2=cast(left(Col2,charindex(',',Col2+',')-1) as nvarchar(100)),Split=cast(stuff(COl2+',',1,charindex(',',Col2+','),'') as nvarchar(100)) from Tab
union all
select Col1,COl2=cast(left(Split,charindex(',',Split)-1) as nvarchar(100)),Split= cast(stuff(Split,1,charindex(',',Split),'') as nvarchar(100)) from Roy where split>''
)
select COl1,COl2 from roy order by COl1 option (MAXRECURSION 0)
合并列值
--合并列值
--原著:邹建
--改编:爱新觉罗.毓华(十八年风雨,守得冰山雪莲花开) 2007-12-16 广东深圳
--表结构,数据如下:
/*
id value
----- ------
1 aa
1 bb
2 aaa
2 bbb
2 ccc
*/
--需要得到结果:
/*
id values
------ -----------
1 aa,bb
2 aaa,bbb,ccc
即:group by id, 求value 的和(字符串相加)
*/
--1. 旧的解决方法(在sql server 2000中只能用函数解决。)
--1. 创建处理函数
create table tb(id int, value varchar(10))
insert into tb values(1, 'aa')
insert into tb values(1, 'bb')
insert into tb values(2, 'aaa')
insert into tb values(2, 'bbb')
insert into tb values(2, 'ccc')
go
create function dbo.f_str(@id int)
returns varchar(8000)
as
begin
declare @r varchar(8000)
set @r = ''
select @r = @r + ',' + value from tb where id=@id
return stuff(@r, 1, 1, '')
end
go
-- 调用函数
SELECt id, value = dbo.f_str(id) FROM tb GROUP BY id
drop table tb
drop function dbo.f_str
/*
id value
----------- -----------
1 aa,bb
2 aaa,bbb,ccc
(所影响的行数为2 行)
*/
--2、另外一种函数.
create table tb(id int, value varchar(10))
insert into tb values(1, 'aa')
insert into tb values(1, 'bb')
insert into tb values(2, 'aaa')
insert into tb values(2, 'bbb')
insert into tb values(2, 'ccc')
go
--创建一个合并的函数
create function f_hb(@id int)
returns varchar(8000)
as
begin
declare @str varchar(8000)
set @str = ''
select @str = @str + ',' + cast(value as varchar) from tb where id = @id
set @str = right(@str , len(@str) - 1)
return(@str)
End
go
--调用自定义函数得到结果:
select distinct id ,dbo.f_hb(id) as value from tb
drop table tb
drop function dbo.f_hb
/*
id value
----------- -----------
1 aa,bb
2 aaa,bbb,ccc
(所影响的行数为2 行)
*/
--2. 新的解决方法(在sql server 2005中用OUTER APPLY等解决。)
create table tb(id int, value varchar(10))
insert into tb values(1, 'aa')
insert into tb values(1, 'bb')
insert into tb values(2, 'aaa')
insert into tb values(2, 'bbb')
insert into tb values(2, 'ccc')
go
-- 查询处理
select * from(select distinct id from tb)a outer apply(
select [values]= stuff(replace(replace(
(
select value from tb n
where id = a.id
for xml auto
), ' <N value="', ','), '"/>', ''), 1, 1, '')
)N
drop table tb
/*
id values
----------- -----------
1 aa,bb
2 aaa,bbb,ccc
(2 行受影响)
*/
--SQL2005中的方法
create table tb(id int, value varchar(10))
insert into tb values(1, 'aa')
insert into tb values(1, 'bb')
insert into tb values(2, 'aaa')
insert into tb values(2, 'bbb')
insert into tb values(2, 'ccc')
go
select id, [values]=stuff((select ','+[value] from tb t where id=tb.id for xml path('')), 1, 1, '')
from tb
group by id
/*
id values
----------- --------------------
1 aa,bb
2 aaa,bbb,ccc
(2 row(s) affected)
*/
drop table tb
分拆列值
--分拆列值
--原著:邹建
--改编:爱新觉罗.毓华(十八年风雨,守得冰山雪莲花开) 2007-12-16 广东深圳
/*
有表tb, 如下:
id value
----------- -----------
1 aa,bb
2 aaa,bbb,ccc
*/
--欲按id,分拆value列, 分拆后结果如下:
/*
id value
----------- --------
1 aa
1 bb
2 aaa
2 bbb
2 ccc
*/
--1. 旧的解决方法(sql server 2000)
select top 8000 id = identity(int, 1, 1) into # from syscolumns a, syscolumns b
select A.id, substring(A.[values], B.id, charindex(',', A.[values] + ',', B.id) - B.id)
from tb A, # B
where substring(',' + A.[values], B.id, 1) = ','
drop table #
--2. 新的解决方法(sql server 2005)
create table tb(id int,value varchar(30))
insert into tb values(1,'aa,bb')
insert into tb values(2,'aaa,bbb,ccc')
go
select A.id, B.value
from(
select id, [value] = convert(xml,' <root> <v>' + replace([value], ',', ' </v> <v>') + ' </v> </root>') from tb
)A
outer apply(
select value = N.v.value('.', 'varchar(100)') from A.[value].nodes('/root/v') N(v)
)B
drop table tb
/*
id value
----------- ------------------------------
1 aa
1 bb
2 aaa
2 bbb
2 ccc
(5 行受影响)
*/
最好是像之前那些帖子 经典例子一小个的好...老长的代码 看到就怕..
--合并分拆表
/******************************************************************************************************************************************************
合并分拆表数据
整理人:中国风(Roy)
日期:2008.06.06
******************************************************************************************************************************************************/
--> --> (Roy)生成測試數據
if not object_id('Tab') is null
drop table Tab
Go
Create table Tab([Col1] int,[Col2] nvarchar(1))
Insert Tab
select 1,N'a' union all
select 1,N'b' union all
select 1,N'c' union all
select 2,N'd' union all
select 2,N'e' union all
select 3,N'f'
Go
合并表:
SQL2000用函数:
go
if object_id('F_Str') is not null
drop function F_Str
go
create function F_Str(@Col1 int)
returns nvarchar(100)
as
begin
declare @S nvarchar(100)
select @S=isnull(@S+',','')+Col2 from Tab where Col1=@Col1
return @S
end
go
Select distinct Col1,Col2=dbo.F_Str(Col1) from Tab
go
SQL2005用XML:
方法1:
select
a.Col1,Col2=stuff(b.Col2.value('/R[1]','nvarchar(max)'),1,1,'')
from
(select distinct COl1 from Tab) a
Cross apply
(select COl2=(select N','+Col2 from Tab where Col1=a.COl1 For XML PATH(''), ROOT('R'), TYPE))b
方法2:
select
a.Col1,COl2=replace(b.Col2.value('/Tab[1]','nvarchar(max)'),char(44)+char(32),char(44))
from
(select distinct COl1 from Tab) a
cross apply
(select Col2=(select COl2 from Tab where COl1=a.COl1 FOR XML AUTO, TYPE)
.query('<Tab>
{for $i in /Tab[position()<last()]/@COl2 return concat(string($i),",")}
{concat("",string(/Tab[last()]/@COl2))}
</Tab>')
)b
SQL05用CTE:
;with roy as(select Col1,Col2,row=row_number()over(partition by COl1 order by COl1) from Tab)
,Roy2 as
(select COl1,cast(COl2 as nvarchar(100))COl2,row from Roy where row=1
union all
select a.Col1,cast(b.COl2+','+a.COl2 as nvarchar(100)),a.row from Roy a join Roy2 b on a.COl1=b.COl1 and a.row=b.row+1)
select Col1,Col2 from Roy2 a where row=(select max(row) from roy where Col1=a.COl1) order by Col1 option (MAXRECURSION 0)
生成结果:
/*
Col1 COl2
----------- ------------
1 a,b,c
2 d,e
3 f
(3 行受影响)
*/
拆分表:
--> --> (Roy)生成測試數據
if not object_id('Tab') is null
drop table Tab
Go
Create table Tab([Col1] int,[COl2] nvarchar(5))
Insert Tab
select 1,N'a,b,c' union all
select 2,N'd,e' union all
select 3,N'f'
Go
SQL2000用辅助表:
if object_id('Tempdb..#Num') is not null
drop table #Num
go
select top 100 ID=Identity(int,1,1) into #Num from syscolumns a,syscolumns b
Select
a.Col1,COl2=substring(a.Col2,b.ID,charindex(',',a.Col2+',',b.ID)-b.ID)
from
Tab a,#Num b
where
charindex(',',','+a.Col2,b.ID)=b.ID --也可用 substring(','+a.COl2,b.ID,1)=','
SQL2005用Xml:
select
a.COl1,b.Col2
from
(select Col1,COl2=convert(xml,'<root><v>'+replace(COl2,',','</v><v>')+'</v></root>') from Tab)a
outer apply
(select Col2=C.v.value('.','nvarchar(100)') from a.COl2.nodes('/root/v')C(v))b
SQL05用CTE:
;with roy as
(select Col1,COl2=cast(left(Col2,charindex(',',Col2+',')-1) as nvarchar(100)),Split=cast(stuff(COl2+',',1,charindex(',',Col2+','),'') as nvarchar(100)) from Tab
union all
select Col1,COl2=cast(left(Split,charindex(',',Split)-1) as nvarchar(100)),Split= cast(stuff(Split,1,charindex(',',Split),'') as nvarchar(100)) from Roy where split>''
)
select COl1,COl2 from roy order by COl1 option (MAXRECURSION 0)
生成结果:
/*
Col1 COl2
----------- -----
1 a
1 b
1 c
2 d
2 e
3 f
*/
/*
标题:普通行列转换(version 2.0)
作者:爱新觉罗.毓华(十八年风雨,守得冰山雪莲花开)
时间:-03-09
地点:广东深圳
说明:普通行列转换(version 1.0)仅针对sql server 2000提供静态和动态写法,version 2.0增加sql server 2005的有关写法。
问题:假设有张学生成绩表(tb)如下:
姓名课程分数
张三语文74
张三数学83
张三物理93
李四语文74
李四数学84
李四物理94
想变成(得到如下结果):
姓名语文数学物理
---- ---- ---- ----
李四74 84 94
张三74 83 93
-------------------
*/
create table tb(姓名varchar(10) , 课程varchar(10) , 分数int)
insert into tb values('张三' , '语文' , 74)
insert into tb values('张三' , '数学' , 83)
insert into tb values('张三' , '物理' , 93)
insert into tb values('李四' , '语文' , 74)
insert into tb values('李四' , '数学' , 84)
insert into tb values('李四' , '物理' , 94)
go
--SQL SERVER 2000 静态SQL,指课程只有语文、数学、物理这三门课程。(以下同)
select 姓名as 姓名,
max(case 课程when '语文' then 分数else 0 end) 语文,
max(case 课程when '数学' then 分数else 0 end) 数学,
max(case 课程when '物理' then 分数else 0 end) 物理
from tb
group by 姓名
--SQL SERVER 2000 动态SQL,指课程不止语文、数学、物理这三门课程。(以下同)
declare @sql varchar(8000)
set @sql = 'select 姓名'
select @sql = @sql + ' , max(case 课程when ''' + 课程+ ''' then 分数else 0 end) [' + 课程+ ']'
from (select distinct 课程from tb) as a
set @sql = @sql + ' from tb group by 姓名'
exec(@sql)
--SQL SERVER 2005 静态SQL。
select * from (select * from tb) a pivot (max(分数) for 课程in (语文,数学,物理)) b
--SQL SERVER 2005 动态SQL。
declare @sql varchar(8000)
select @sql = isnull(@sql + '],[' , '') + 课程from tb group by 课程
set @sql = '[' + @sql + ']'
exec ('select * from (select * from tb) a pivot (max(分数) for 课程in (' + @sql + ')) b')
---------------------------------
/*
问题:在上述结果的基础上加平均分,总分,得到如下结果:
姓名语文数学物理平均分总分
---- ---- ---- ---- ------ ----
李四74 84 94 84.00 252
张三74 83 93 83.33 250
*/
--SQL SERVER 2000 静态SQL。
select 姓名姓名,
max(case 课程when '语文' then 分数else 0 end) 语文,
max(case 课程when '数学' then 分数else 0 end) 数学,
max(case 课程when '物理' then 分数else 0 end) 物理,
cast(avg(分数*1.0) as decimal(18,2)) 平均分,
sum(分数) 总分
from tb
group by 姓名
--SQL SERVER 2000 动态SQL。
declare @sql varchar(8000)
set @sql = 'select 姓名'
select @sql = @sql + ' , max(case 课程when ''' + 课程+ ''' then 分数else 0 end) [' + 课程+ ']'
from (select distinct 课程from tb) as a
set @sql = @sql + ' , cast(avg(分数*1.0) as decimal(18,2)) 平均分, sum(分数) 总分from tb group by 姓名'
exec(@sql)
--SQL SERVER 2005 静态SQL。
select m.* , n.平均分, n.总分from
(select * from (select * from tb) a pivot (max(分数) for 课程in (语文,数学,物理)) b) m,
(select 姓名, cast(avg(分数*1.0) as decimal(18,2)) 平均分, sum(分数) 总分from tb group by 姓名) n
where m.姓名= n.姓名
--SQL SERVER 2005 动态SQL。
declare @sql varchar(8000)
select @sql = isnull(@sql + ',' , '') + 课程from tb group by 课程
exec ('select m.* , n.平均分, n.总分from
(select * from (select * from tb) a pivot (max(分数) for 课程in (' + @sql + ')) b) m ,
(select 姓名, cast(avg(分数*1.0) as decimal(18,2)) 平均分, sum(分数) 总分from tb group by 姓名) n
where m.姓名= n.姓名')
drop table tb
------------------
------------------
/*
问题:如果上述两表互相换一下:即表结构和数据为:
姓名语文数学物理
张三74
李四74
想变成(得到如下结果):
姓名课程分数
---- ---- ----
李四语文74
李四数学84
李四物理94
张三语文74
张三数学83
张三物理93
--------------
*/
create table tb(姓名varchar(10) , 语文int , 数学int , 物理int)
insert into tb values('张三',74,83,93)
insert into tb values('李四',74,84,94)
go
--SQL SERVER 2000 静态SQL。
select * from
(
select 姓名, 课程= '语文' , 分数= 语文from tb
union all
select 姓名, 课程= '数学' , 分数= 数学from tb
union all
select 姓名, 课程= '物理' , 分数= 物理from tb
) t
order by 姓名, case 课程when '语文' then 1 when '数学' then 2 when '物理' then 3 end
--SQL SERVER 2000 动态SQL。
--调用系统表动态生态。
declare @sql varchar(8000)
select @sql = isnull(@sql + ' union all ' , '' ) + ' select 姓名, [课程] = ' + quotename(Name , '''') + ' , [分数] = ' + quotename(Name) + ' from tb'
from syscolumns
where name! = N'姓名' and ID = object_id('tb') --表名tb,不包含列名为姓名的其它列
order by colid asc
exec(@sql + ' order by 姓名')
--SQL SERVER 2005 动态SQL。
select 姓名, 课程, 分数from tb unpivot (分数for 课程in([语文] , [数学] , [物理])) t
--SQL SERVER 2005 动态SQL,同SQL SERVER 2000 动态SQL。
--------------------
/*
问题:在上述的结果上加个平均分,总分,得到如下结果:
姓名课程 分数
---- ------ ------
李四语文 74.00
李四数学 84.00
李四物理 94.00
李四平均分84.00
李四总分 252.00
张三语文 74.00
张三数学 83.00
张三物理 93.00
张三平均分83.33
张三总分 250.00
------------------
*/
select * from
(
select 姓名as 姓名, 课程= '语文' , 分数= 语文from tb
union all
select 姓名as 姓名, 课程= '数学' , 分数= 数学from tb
union all
select 姓名as 姓名, 课程= '物理' , 分数= 物理from tb
union all
select 姓名as 姓名, 课程= '平均分' , 分数= cast((语文+ 数学+ 物理)*1.0/3 as decimal(18,2)) from tb
union all
select 姓名as 姓名, 课程= '总分' , 分数= 语文+ 数学+ 物理from tb
) t
order by 姓名, case 课程when '语文' then 1 when '数学' then 2 when '物理' then 3 when '平均分' then 4 when '总分' then 5 end
drop table tb
--给个一列多用的
--原创不详
一张表 t 字段 team
'a'
'b',
'c',
'd'
任意组合
结果
a,b
a,c
a,d
b,c
b,d
c,d
怎么写
create table t(team varchar(1))
insert into t values('a')
insert into t values('b')
insert into t values('c')
insert into t values('d')
go
select m.team , n.team from t m, t n where m.team < n.team order by m.team
drop table t
/*
team team
---- ----
a b
a c
a d
b c
b d
c d
(所影响的行数为 6 行)
*/