经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » MS SQL Server » 查看文章
SQL SERVER 实现多行转多列
来源:cnblogs  作者:尾随前行  时间:2019/8/2 8:45:52  对本文有异议

有这样一个需求,一个表单主表,一个扩展列表,查询的时候要把扩展列表中的多行转成主表多列。

比如

dt_zhubiao [主表]

id type title
1 1 表单1-1
2 1 表单1-2
3 2 表单2-1
4 2 表单2-2

dt_kuozhanbiao [扩展表]

id formid name title value
1 1 ext_a 工龄 18
2 1 ext_b 职称 副级
3 2 ext_a 工龄 20
4 2 ext_b 职称 正级
5 3 ext_2a 字段1 值1
6 3 ext_2b 字段2 值2
7 3 ext_2c 字段3 值3
8 4 ext_2a 字段1 值21
9 4 ext_2b 字段2 值22
10 4 ext_2c 字段3 值23

查询时,会根据dt_zhubiao表的type来查询,type字段一样时,dt_kuozhanbiao表条数和name都会一致,value不一致。

想要的结果如下:

查询type=1时,select * from dt_zhubiao where type = 1 ... 

id type title ext_a ext_b
1 1 表单1-1 18 副级
2 1 表单1-2 20 正级

查询type=2时,select * from dt_zhubiao where type =2 ...

id type title ext_2a ext_2b ext_2c
3 2 表单2-1 值1 值2 值3
4 2 表单2-2 值21 值22 值23

 

那么问题来了,基于 select * from dt_zhubiao where type = ? 基础sql语句,如何生成这种查询结果 ? 

这个问题应该多用于动态表单,之前自己尝试过join 、union去解决,都总差那么点意思。 -:)

后面去多个论坛发帖挨个问了个遍,终于寻到解决办法了。SQL 代码如下:

  1. --测试数据
  2. if not object_id(N'Tempdb..#主表') is null
  3. drop table #主表
  4. Go
  5. Create table #主表([id] int,[type] int,[title] nvarchar(25))
  6. Insert #主表
  7. select 1,1,N'表单1-1' union all
  8. select 2,1,N'表单1-2' union all
  9. select 3,2,N'表单2-1' union all
  10. select 4,2,N'表单2-2'
  11. GO
  12. if not object_id(N'Tempdb..#扩展表') is null
  13. drop table #扩展表
  14. Go
  15. Create table #扩展表([id] int,[formid] int,[name] nvarchar(26),[title] nvarchar(23),[value] nvarchar(22))
  16. Insert #扩展表
  17. select 1,1,N'ext_a',N'工龄',N'18' union all
  18. select 2,1,N'ext_b',N'职称',N'副级' union all
  19. select 3,2,N'ext_1',N'工龄',N'18' union all
  20. select 4,2,N'ext_b',N'职称',N'正级' union all
  21. select 5,3,N'ext_2a',N'字段1',N'值1' union all
  22. select 6,3,N'ext_2b',N'字段2',N'值2' union all
  23. select 7,3,N'ext_2c',N'字段3',N'值3'union all
  24. select 8,4,N'ext_2a',N'字段1',N'值1' union all
  25. select 9,4,N'ext_2b',N'字段2',N'值2' union all
  26. select 10,4,N'ext_2c',N'字段3',N'值3'
  27. Go
  28. --测试数据结束
  29. DECLARE @sql VARCHAR(MAX)
  30. SET @sql = 'select #主表.id,#主表.type,#主表.title'
  31. SELECT @sql = @sql + ',max(case name when ''' + name
  32. + ''' then [value] else null end)[' + name + ']'
  33. FROM ( Select DISTINCT name from #扩展表 JOIN #主表 ON formid IN (SELECT id FROM #主表 WHERE type=2)
  34. ) a
  35. SET @sql = @sql
  36. + ' from #扩展表 JOIN #主表 ON formid =#主表.id WHERE type=2 group by #主表.id,#主表.type,#主表.title'
  37. EXEC(@sql)

至此已经解决了我的问题,但是对于实际的项目运用还是缺少点什么,比如 分页、条件筛选。那么得在此基础上稍微修改一下,这个简单我自己会做了  -:) 。

  1. DECLARE @sql VARCHAR(MAX)
  2. SET @sql = 'with tb as (select ROW_NUMBER() OVER(Order by #主表.id ) AS rindex,#主表.id,#主表.type,#主表.title'
  3. SELECT @sql = @sql + ',max(case name when ''' + name
  4. + ''' then [value] else null end)[' + name + ']'
  5. FROM ( Select DISTINCT name from #扩展表 JOIN #主表 ON formid IN (SELECT id FROM #主表 WHERE type=2)
  6. ) a
  7. SET @sql = @sql
  8. + ' from #扩展表 JOIN #主表 ON formid =#主表.id WHERE type=2 group by #主表.id,#主表.type,#主表.title); select * from tb where ext_2b =''值1'' and rindex between 1 and 10 ' --拼接with 及查询条件
  9. EXEC(@sql)

到这自我感觉应该差不多了,再改改做成个存储过程应该可以用了,但是吧,想着用程序也去实现一遍,看看哪种实行起来更方便 -:) 

程序实现两种思路,一种组装table:先结合分页、条件筛选等查出需要的主表数据集得到一个datatable,然后给datatable动态去添加对应列,然后循环去赋值(这种情况就无法实现针对扩展字段进行排序)。

  1. DataTable tb = DbHelperSQL.Query("with tb as (select row_number over(order by #主表.id) as rindex,* from #主表 where id in (select formid from #扩展表 where value ='值2') adn type=2 );select * from tb where rindex between 1 and 10").Tables[0];
  2. var ar = new System.Collections.ArrayList();
  3. foreach (DataRow crow in DbHelperSQL.Query("select name from #扩展表 where formid in (select id from #主表 where type = 2)").Tables[0].Rows) {
  4. ar.Add(crow[0]);
  5. tb.Columns.Add(crow[0].ToString(), typeof(string));
  6. }
  7. //这里可以一次性加载tb数据集中包含的所有#扩展表数据,然后在内存中进行操作赋值,如下循环查询数据库赋值只是为了写的方便
  8. for (var i =0; i <tb.Rows.Count;i++) {
  9. var formid = tb.Rows[i]["id"];
  10. foreach (var ari in ar) {
  11. tb.Rows[i][ari.ToString()] = DbHelperSQL.GetSingle(string.Format("select value from #临时表 where formid ={0} and name ='{1}'",formid,ari));
  12. }
  13. }
  14. return tb;

第二种就是组装SQL语句啦,但是不在程序中组装之前的@sql字符串,但是还是参考之前的SQL代码思路,然后转成程序代码思路。在之前SQL实现的代码中,最后的exec(@sql)前一行加上打印@sql的语句,就会得到最后执行的sql语句

  1. select #主表.id,#主表.type,#主表.title
  2. ,max(case name when 'ext_2a' then [value] else null end)[ext_2a]
  3. ,max(case name when 'ext_2b' then [value] else null end)[ext_2b]
  4. ,max(case name when 'ext_2c' then [value] else null end)[ext_2c]
  5. from #扩展表 JOIN #主表 ON formid =#主表.id WHERE type=2 group by #主表.id,#主表.type,#主表.title

基于这个SQL语句用程序去拼接最终的SQL语句执行(包括分页、条件筛选)。

  1. string sql=" with tb as (select row_number over(order by #主表.id) rindex, #主表.id,#主表.type,#主表.title";
  2. foreach (DataRow crow in DbHelperSQL.Query("select name from #扩展表 where formid in (select id from #主表 where type = 2) group by name").Tables[0].Rows) {
  3. sql +=",max(case name when '"+crow["name"]+"' then [value] else null end) "+crow["name"];
  4. }
  5. sql+=" from #扩展表 join #主表 on formid = #主表.id where type = 2 group by #主表.id,#主表.type,#主表.title); select * from tb where ext_2a ='值1' and rindex between 1 and 10 ";
  6. return DbHelperSQL.Query(sql).Tables[0];
  7. //此处单纯的SQL语句拼接,也可以再次优化实现动态参数化。

这种情况是可以实现所有的列都能进行排序。 目前来讲,还只是去实现这个功能,还没有考虑性能优化之类的。

论坛原咨询帖:https://bbs.csdn.net/topics/392999794 。 记于此以作备份,好记性不如烂笔头。 -:)

 

原文链接:http://www.cnblogs.com/tohert/p/11283723.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号