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

比如

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 代码如下:

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

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

DECLARE @sql VARCHAR(MAX)
SET @sql = 'with tb as (select ROW_NUMBER() OVER(Order by #主表.id ) AS rindex,#主表.id,#主表.type,#主表.title'
SELECT @sql = @sql + ',max(case name when ''' + name
+ ''' then [value] else null end)[' + name + ']'
FROM ( Select DISTINCT name from #扩展表 JOIN #主表 ON formid IN (SELECT id FROM #主表 WHERE type=2)
) a
SET @sql = @sql
+ ' 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 及查询条件
EXEC(@sql)

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

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

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[];
var ar = new System.Collections.ArrayList();
foreach (DataRow crow in DbHelperSQL.Query("select name from #扩展表 where formid in (select id from #主表 where type = 2)").Tables[].Rows) {
ar.Add(crow[]);
tb.Columns.Add(crow[].ToString(), typeof(string));
}
//这里可以一次性加载tb数据集中包含的所有#扩展表数据,然后在内存中进行操作赋值,如下循环查询数据库赋值只是为了写的方便
for (var i =0; i <tb.Rows.Count;i++) {
var formid = tb.Rows[i]["id"];
foreach (var ari in ar) {
tb.Rows[i][ari.ToString()] = DbHelperSQL.GetSingle(string.Format("select value from #临时表 where formid ={0} and name ='{1}'",formid,ari));
}
}
return tb;

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

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

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

string sql=" with tb as (select row_number over(order by #主表.id) rindex, #主表.id,#主表.type,#主表.title";
foreach (DataRow crow in DbHelperSQL.Query("select name from #扩展表 where formid in (select id from #主表 where type = 2) group by name").Tables[].Rows) {
sql +=",max(case name when '"+crow["name"]+"' then [value] else null end) "+crow["name"];
}
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 ";
return DbHelperSQL.Query(sql).Tables[];
//此处单纯的SQL语句拼接,也可以再次优化实现动态参数化。

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

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

SQL SERVER 实现多行转多列的更多相关文章

  1. Sql server 中将数据行转列列转行(二)

    老规矩,先弄一波测试数据,数据填充代码没有什么意义,先折叠起来: /* 第一步:创建临时表结构 */ CREATE TABLE #Student --创建临时表 ( StuName ), --学生名称 ...

  2. 在SQL Server 2014里可更新的列存储索引 (Updateable Column Store Indexes)

    传统的关系数据库服务引擎往往并不是对超大量数据进行分析计算的最佳平台,为此,SQL Server中开发了分析服务引擎去对大笔数据进行分析计算.当然,对于数据的存放平台SQL Server数据库引擎而言 ...

  3. 通过DBCC Page查看在SQL Server中哪行数据被锁住了?

    原文:通过DBCC Page查看在SQL Server中哪行数据被锁住了? 如何查看被锁的是哪行数据?通过dbcc page可以. 要想明白这个问题: 首先,需要模拟阻塞问题,这里直接模拟了阻塞问题的 ...

  4. SQL Server删除重复行的6个方法

    SQL Server删除重复行是我们最常见的操作之一,下面就为您介绍六种适合不同情况的SQL Server删除重复行的方法,供您参考. 1.如果有ID字段,就是具有唯一性的字段 delect   ta ...

  5. SQL Server获取指定行的数据

    SQL Server获取指定行(如第二行)的数据   --SQL Server获取指定行(如第二行)的数据-- --法一(对象法)-- select * from ( select * , numbe ...

  6. 向SQL Server 现有表中添加新列并添加描述.

    注: sql server 2005 及以上支持. 版本估计是不支持(工作环境2005,2008). 工作需要, 需要向SQL Server 现有表中添加新列并添加描述. 从而有个如下存储过程. (先 ...

  7. SQL Server 中 ROWLOCK 行级锁

    一.ROWLOCK的使用 1.ROWLOCK行级锁确保,在用户取得被更新的行,到该行进行更新,这段时间内不被其它用户所修改.因而行级锁即可保证数据的一致性,又能提高数据操作的并发性. 2.ROWLOC ...

  8. SQL SERVER pivot(行转列),unpivot(列转行)

    [pivot]行转列:多行变一列 假设学生成绩表Score1 Name Subject Score 小张 语文 88 小花 数学 89 小张 数学 90 Name 语文 数学 小花 null 89 小 ...

  9. 在一个SQL Server表中的多个列找出最大值

    在一个SQL Server表中一行的多个列找出最大值 有时候我们需要从多个相同的列里(这些列的数据类型相同)找出最大的那个值,并显示 这里给出一个例子 IF (OBJECT_ID('tempdb..# ...

随机推荐

  1. Luogu4688 [Ynoi2016]掉进兔子洞 【莫队,bitset】

    题目链接:洛谷 我们知道要求的是\([l_1,r_1],[l_2,r_2],[l_3,r_3]\)的可重集取交的大小,肯定是要用bitset的,那怎么做可重集呢? 那就是要稍微动点手脚,首先在离散化的 ...

  2. 利用 Python 尝试采用面向对象的设计方法计算图形面积及周长

    利用 Python 尝试采用面向对象的设计方法.(1)设计一个基类 Shape:包含两个成员函数:def cal_area(): 计算并返回该图形的面积,保留两位小数:def cal_perimete ...

  3. 数据结构实验之图论十一:AOE网上的关键路径【Bellman_Ford算法】

    Problem Description 一个无环的有向图称为无环图(Directed Acyclic Graph),简称DAG图.     AOE(Activity On Edge)网:顾名思义,用边 ...

  4. ROS手动编写服务端和客户端service demo(C++)

    service demo 原理和 topic 通信方式很像 点击打开链接,因此 1.srv : 进入 service_demo 创建 srv 文件夹,创建 Greeting.srv,将以下代码插入: ...

  5. Jetbrain全栈最新激活方法(2019年及之前所有新老版本)

    随着2019版的到来,之前的永久激活教程也不生效了,所以今天为大家带来一种新的永久激活方式. 1.下载新版破解补丁 破解补丁传送门提取码:3e8j 点击传送门下载补丁文件 jetbrains-agen ...

  6. 10月清北学堂培训 Day 3

    今天是钟皓曦老师的讲授~ zhx:题很简单,就是恶心一些qwq~ T1 别人只删去一个字符都能AC,我双哈希+并查集只有40?我太菜了啊qwq 考虑到越短的字符串越难压缩,越长的字符串越好压缩,所以我 ...

  7. Web API 跨域

    1. NuGet下载## microsoft.aspnet.webapi.cors 2 . Web API 路由中 config.EnableCors(new EnableCorsAttribute( ...

  8. @AUTORELEASEPOOL

    Swift 在内存管理上使用的是自动引用计数 (ARC) 的一套方法,在 ARC 中虽然不需要手动地调用像是 retain,release 或者是 autorelease 这样的方法来管理引用计数,但 ...

  9. Bsgs模板

    模板最主要的是自己看得舒服,不会给自己留隐患,调起来比较简单,板子有得是,最主要的是改造出适合你的那一套.                  ——mzz #include<bits/stdc++ ...

  10. Uber如何搭建一个基于Kafka的跨数据中心复制平台 原创: 徐宏亮 AI前线 今天

    Uber如何搭建一个基于Kafka的跨数据中心复制平台 原创: 徐宏亮 AI前线 今天