• 全部
  • 基础类
  • 应用实例
  • 新技术前沿

小菜提问一个觉得比较复杂的sql 倾囊。。。

irye 2008-03-21 02:43:37
一个表是这样的

其中职业是有限的个数是1-9的任意数 不然这个转化后的表的列数就不一定了 但是城市可以认为是无限多种的 也就是转化后的行可以无限多

这个问题很重要 请高手解答 俺倾囊了。。。



1 战士 杭州
2 法师 平顶山
3 牧师 南京
4 盗贼 北京
5 术士 杭州
6 萨满 杭州
7 德鲁伊 上海
8 圣骑士 北京
9 猎人 悉尼

转为下面的表

1 战士 5 术士 6 萨满 杭州
4 盗贼 8 圣骑士 北京
3 牧师 南京
9 猎人 悉尼
7 德鲁伊 上海
2 法师 平顶山
...全文
83 点赞 收藏 16
写回复
16 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
一品梅 2008-03-21
呵呵,又见xx端倪。
回复
wzy_love_sly 2008-03-21
梁哥太有名了 哈哈
回复
irye 2008-03-21
wzy_love_sly 咋了

多谢liangCK 。。
回复
liangCK 2008-03-21
[Quote=引用 11 楼 wzy_love_sly 的回复:]
咋回事?日
[/Quote]

不知道.
回复
liangCK 2008-03-21
create table tb(id int,type varchar(50),address varchar(50))
insert tb select 1 ,'战士' , '杭州'
insert tb select 2 ,'法师' ,'平顶山'
insert tb select 3 ,'牧师' , '南京'
insert tb select 4 ,'盗贼' , '北京'
insert tb select 5 ,'术士' , '杭州'
insert tb select 6 ,'萨满' , '杭州'
insert tb select 7 ,'德鲁伊' , '上海'
insert tb select 8 ,'圣骑士' , '北京'
insert tb select 9 ,'猎人' , '悉尼'

declare @sql varchar(8000)
set @sql=''
select @sql=@sql+',max(case when px='+rtrim(px)+' then type else '''' end) [f'+rtrim(px)+']'
from (select distinct px=(select count(*) from tb where address=a.address and id<a.id)+1 from tb a) t
set @sql='select '+stuff(@sql,1,1,'')+',address from (select *,px=(select count(*) from tb where address=a.address and id<a.id)+1 from tb a) t group by address order by address'
exec ( @sql )

drop table tb

/*
f1 f2 f3 address
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- --------------------------------------------------
盗贼 圣骑士 北京
战士 术士 萨满 杭州
牧师 南京
法师 平顶山
德鲁伊 上海
猎人 悉尼

(6 行受影响)
*/
回复
wzy_love_sly 2008-03-21
咋回事?日
回复
irye 2008-03-21
呵呵 意思就是这个意思 只要逻辑对了就行 行数没限制的

请问 liangCK老大 您的输出是测试结果吗 我手头没有测试环境 不是的话我找个环境试试 多谢您了
回复
irye 2008-03-21
在原表中是数字是唯一索引 但转换后就只是标识了
如原表也可能是

1 战士 杭州
2 法师 平顶山
3 牧师 南京
4 盗贼 北京
5 术士 杭州
6 萨满 杭州
7 德鲁伊 上海
8 圣骑士 北京
9 猎人 悉尼
10 萨满 杭州
11 德鲁伊 上海
12 圣骑士 北京
13 猎人 悉尼
14 德鲁伊 北京
15 术士 杭州
16 德鲁伊 杭州
17 猎人 上海
18 猎人 北京
19 德鲁伊 悉尼
20 萨满 杭州
21 德鲁伊 上海

回复
wzy_love_sly 2008-03-21
4个杭州啊?你结果怎么三个
回复
wzy_love_sly 2008-03-21

create table tb(name varchar(10),ads varchar(10))
insert into tb select '战士','杭州'
insert into tb select '法师','平顶山'
insert into tb select '牧师','北京'
insert into tb select '盗贼','杭州'
insert into tb select '术士','杭州'

insert into tb select '萨满','杭州'
insert into tb select '德鲁伊','上海'
insert into tb select '圣骑士','北京'
insert into tb select '猎人','悉尼'

select *,px=(select count(1) from tb where ads=t.ads and name<t.name)+1 into # from tb t

select * from # order by ads,px

declare @sql varchar(8000)
set @sql = 'select ads as ' + '地名'
select @sql = @sql + ' , max(case px when ''' + ltrim(px) + ''' then name else '''' end) [' + ltrim(px) + ']'
from (select distinct px from #) as a
set @sql = @sql + ' from # group by ads'
exec(@sql)


地名 1 2 3 4
北京 牧师 圣骑士
杭州 盗贼 萨满 术士 战士
平顶山 法师
上海 德鲁伊
悉尼 猎人
回复
irye 2008-03-21
谢谢 数字不是一列 是与职业一起的就行
回复
liangCK 2008-03-21
/*
标题:普通行列转换(version 2.0)
作者:爱新觉罗.毓华
时间:2008-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 课程
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  83  93
李四 74  84  94
想变成(得到如下结果):
姓名 课程 分数
---- ---- ----
李四 语文 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
回复
liangCK 2008-03-21
行列转换?
回复
wzy_love_sly 2008-03-21
行列转换
回复
liangCK 2008-03-21
1 战士 杭州
2 法师 平顶山
3 牧师 南京
4 盗贼 北京
5 术士 杭州
6 萨满 杭州
7 德鲁伊 上海
8 圣骑士 北京
9 猎人 悉尼

1 战士 5 术士 6 萨满 杭州
4 盗贼 8 圣骑士 北京
3 牧师 南京
9 猎人 悉尼
7 德鲁伊 上海
2 法师 平顶山

-------------
请问这个..数字是一列吗?..还是跟那个战士..什么的是一列的?
回复
liangCK 2008-03-21
3.3 各种字符串合并处理示例.sql

--各种字符串分函数

--3.3.1 使用游标法进行字符串合并处理的示例。
--处理的数据
CREATE TABLE tb(col1 varchar(10),col2 int)
INSERT tb SELECT 'a',1
UNION ALL SELECT 'a',2
UNION ALL SELECT 'b',1
UNION ALL SELECT 'b',2
UNION ALL SELECT 'b',3

--合并处理
--定义结果集表变量
DECLARE @t TABLE(col1 varchar(10),col2 varchar(100))

--定义游标并进行合并处理
DECLARE tb CURSOR LOCAL
FOR
SELECT col1,col2 FROM tb ORDER BY col1,col2
DECLARE @col1_old varchar(10),@col1 varchar(10),@col2 int,@s varchar(100)
OPEN tb
FETCH tb INTO @col1,@col2
SELECT @col1_old=@col1,@s=''
WHILE @@FETCH_STATUS=0
BEGIN
IF @col1=@col1_old
SELECT @s=@s+','+CAST(@col2 as varchar)
ELSE
BEGIN
INSERT @t VALUES(@col1_old,STUFF(@s,1,1,''))
SELECT @s=','+CAST(@col2 as varchar),@col1_old=@col1
END
FETCH tb INTO @col1,@col2
END
INSERT @t VALUES(@col1_old,STUFF(@s,1,1,''))
CLOSE tb
DEALLOCATE tb
--显示结果并删除测试数据
SELECT * FROM @t
DROP TABLE tb
/*--结果
col1 col2
---------- -----------
a 1,2
b 1,2,3
--*/
GO


/*==============================================*/


--3.3.2 使用用户定义函数,配合SELECT处理完成字符串合并处理的示例
--处理的数据
CREATE TABLE tb(col1 varchar(10),col2 int)
INSERT tb SELECT 'a',1
UNION ALL SELECT 'a',2
UNION ALL SELECT 'b',1
UNION ALL SELECT 'b',2
UNION ALL SELECT 'b',3
GO

--合并处理函数
CREATE FUNCTION dbo.f_str(@col1 varchar(10))
RETURNS varchar(100)
AS
BEGIN
DECLARE @re varchar(100)
SET @re=''
SELECT @re=@re+','+CAST(col2 as varchar)
FROM tb
WHERE col1=@col1
RETURN(STUFF(@re,1,1,''))
END
GO

--调用函数
SELECT col1,col2=dbo.f_str(col1) FROM tb GROUP BY col1
--删除测试
DROP TABLE tb
DROP FUNCTION f_str
/*--结果
col1 col2
---------- -----------
a 1,2
b 1,2,3
--*/
GO

/*==============================================*/


--3.3.3 使用临时表实现字符串合并处理的示例
--处理的数据
CREATE TABLE tb(col1 varchar(10),col2 int)
INSERT tb SELECT 'a',1
UNION ALL SELECT 'a',2
UNION ALL SELECT 'b',1
UNION ALL SELECT 'b',2
UNION ALL SELECT 'b',3

--合并处理
SELECT col1,col2=CAST(col2 as varchar(100))
INTO #t FROM tb
ORDER BY col1,col2
DECLARE @col1 varchar(10),@col2 varchar(100)
UPDATE #t SET
@col2=CASE WHEN @col1=col1 THEN @col2+','+col2 ELSE col2 END,
@col1=col1,
col2=@col2
SELECT * FROM #t
/*--更新处理后的临时表
col1 col2
---------- -------------
a 1
a 1,2
b 1
b 1,2
b 1,2,3
--*/
--得到最终结果
SELECT col1,col2=MAX(col2) FROM #t GROUP BY col1
/*--结果
col1 col2
---------- -----------
a 1,2
b 1,2,3
--*/
--删除测试
DROP TABLE tb,#t
GO


/*==============================================*/

--3.3.4.1 每组 <=2 条记录的合并
--处理的数据
CREATE TABLE tb(col1 varchar(10),col2 int)
INSERT tb SELECT 'a',1
UNION ALL SELECT 'a',2
UNION ALL SELECT 'b',1
UNION ALL SELECT 'b',2
UNION ALL SELECT 'c',3

--合并处理
SELECT col1,
col2=CAST(MIN(col2) as varchar)
+CASE
WHEN COUNT(*)=1 THEN ''
ELSE ','+CAST(MAX(col2) as varchar)
END
FROM tb
GROUP BY col1
DROP TABLE tb
/*--结果
col1 col2
---------- ----------
a 1,2
b 1,2
c 3
--*/

--3.3.4.2 每组 <=3 条记录的合并
--处理的数据
CREATE TABLE tb(col1 varchar(10),col2 int)
INSERT tb SELECT 'a',1
UNION ALL SELECT 'a',2
UNION ALL SELECT 'b',1
UNION ALL SELECT 'b',2
UNION ALL SELECT 'b',3
UNION ALL SELECT 'c',3

--合并处理
SELECT col1,
col2=CAST(MIN(col2) as varchar)
+CASE
WHEN COUNT(*)=3 THEN ','
+CAST((SELECT col2 FROM tb WHERE col1=a.col1 AND col2 NOT IN(MAX(a.col2),MIN(a.col2))) as varchar)
ELSE ''
END
+CASE
WHEN COUNT(*)>=2 THEN ','+CAST(MAX(col2) as varchar)
ELSE ''
END
FROM tb a
GROUP BY col1
DROP TABLE tb
/*--结果
col1 col2
---------- ------------
a 1,2
b 1,2,3
c 3
--*/
GO
回复
相关推荐
发帖
MS-SQL Server
创建于2007-09-28

3.3w+

社区成员

MS-SQL Server相关内容讨论专区
申请成为版主
帖子事件
创建了帖子
2008-03-21 02:43
社区公告
暂无公告