SQL Server合并重复行与行列转换(附实例)

优化后的需求:请按照月份查询每个员工该月每天的打卡记录(如下图所示),早上与晚上通过分隔符“||”分割(其实本来的需求是要求回车的,但是我没找到在数据库进行回车的好办法,协商后改成了||,如果看到这篇文章的人知道如何进行分割请告诉我。

表设计:(该需求是2月份因为疫情提出的后加功能,而一开始的打卡表设计是一张灵活的简单表,导致了复杂行列转换的产生)

分析:由表中数据可得,1名员工上下班打两次卡,会产生两条数据。需要将同一天的数据首先进行合并,然后将日期进行行列转换变成表头字段。

1、将同天数据合并(不想看的可以直接看2行列转换):

      a).采用for xml path。for xml path较为详细的用法:点击进入

      b)通过stuff将合并列中的<xxx>去除,日期之间用“||”分割。较为详细的用法:点击进入

我的代码如下:

select pro_dcp_person,(

---进行字符分割
 select stuff((

    --进行重复行合并
     select CONVERT(varchar(20), pro_dcp_date,24)+'||' from pro_dailypunchcard 
     where 1=1 
     and CONVERT(varchar(10), pro_dcp_date, 120) = CONVERT(varchar(10),dcp.pro_dcp_date, 120)
     and pro_dcp_person = dcp.pro_dcp_person for xml path('')
    --合并结束

 ),1,0,'')
---字符分割结束


 )as alldate,CONVERT(varchar(10),dcp.pro_dcp_date, 120)as pro_dcp_date from pro_dailypunchcard dcp 
 where 1=1
 and CONVERT(varchar(10), pro_dcp_date, 120) in ('2020-04-01','2020-04-02','2020-04-03')
 group by pro_dcp_person,CONVERT(varchar(10),dcp.pro_dcp_date, 120)

查询结果如下:

 

2、对如上图所示的查询结果进行直接的行列转换我没有直接成功。

case when 失败代码(复盘的时候懒得再加并没有用的max()了):


select a.*,
case when CONVERT(varchar(10), a.pro_dcp_date, 120) = '2020-04-01' then a.alldate else '/' end as '2020-04-01',
case when CONVERT(varchar(10), a.pro_dcp_date, 120) = '2020-04-02' then a.alldate else '/' end as '2020-04-02',
case when CONVERT(varchar(10), a.pro_dcp_date, 120) = '2020-04-03' then a.alldate else '/' end as '2020-04-03'

 from (

 select pro_dcp_person,(

 select stuff((
 select CONVERT(varchar(20), pro_dcp_date,24)+'||' from pro_dailypunchcard 
 where 1=1 
 and CONVERT(varchar(10), pro_dcp_date, 120) = CONVERT(varchar(10),dcp.pro_dcp_date, 120)
 and pro_dcp_person = dcp.pro_dcp_person for xml path('')
 ),1,0,'')

 )as alldate,CONVERT(varchar(10),dcp.pro_dcp_date, 120)as pro_dcp_date from pro_dailypunchcard dcp 
 
 
 
 where 1=1
 and CONVERT(varchar(10), pro_dcp_date, 120) in ('2020-04-01','2020-04-02','2020-04-03')
 group by pro_dcp_person,CONVERT(varchar(10),dcp.pro_dcp_date, 120)
 
 
 ) as a

case when 失败结果:

↑4月1日至4月3日之间,1079用户的打卡数据应该只有一条,在case when中却出现了三条,合并未成功。

做到这里其实可以把数据传回去在impl层进行数据拆解合并。我最后差一点点要放弃在数据库一步到位了,然后公司年资较长的工程师给我推荐了pivot。

超详细pivot教程:点击进入 pivot有一点点限制,但是对于较为互联网的企业来说不是问题。

但是对于我而言问题并不能直接使用该教程中的存储过程。这篇文章主要也是记录在这种需要对表中数据进行处理,处理之后再行转列的情形。

a)将已经处理的数据转换成视图。

b)在视图中进行行列转换。

因此我需要将我第1步中处理好的数据转换成视图。

转换代码如下:

create VIEW viw_dailypunchcard 
AS 

select pro_dcp_person,(

 select stuff((
 select CONVERT(varchar(20), pro_dcp_date,24)+CHAR(13) from pro_dailypunchcard 
 where 1=1 
 and CONVERT(varchar(10), pro_dcp_date, 120) = CONVERT(varchar(10),dcp.pro_dcp_date, 120)
 and pro_dcp_person = dcp.pro_dcp_person for xml path('')
 ),1,0,'')

 )as alldate,CONVERT(varchar(10),dcp.pro_dcp_date, 120)as pro_dcp_date from pro_dailypunchcard dcp 
 
 
 
 where 1=1
 --and CONVERT(varchar(10), pro_dcp_date, 120) in ('2020-03-01','2020-03-02','2020-03-03')
 group by pro_dcp_person,CONVERT(varchar(10),dcp.pro_dcp_date, 120);

然后在存储过程中进行行列转换,转换代码如下:

DECLARE @dec_val NVARCHAR(MAX)
DECLARE @sql_str NVARCHAR(MAX)
DECLARE @sql_col NVARCHAR(MAX)
DECLARE @tableName SYSNAME --行转列表
DECLARE @groupColumn SYSNAME --分组字段
DECLARE @row2column SYSNAME --行变列的字段
DECLARE @row2columnValue SYSNAME --行变列值的字段
SET @tableName = 'viw_dailypunchcard' --视图名称
SET @groupColumn = 'pro_dcp_person'
SET @row2column = 'pro_dcp_date'
SET @row2columnValue = 'alldate'

--声明要传入的日期
SET @dec_val='''2020-04-01'',''2020-04-02'',''2020-04-03''';

--从行数据中获取可能存在的列
SET @sql_str = N'
SELECT @sql_col_out = ISNULL(@sql_col_out + '','','''') + QUOTENAME(['+@row2column+']) 
    FROM ['+@tableName+'] where CONVERT(varchar(10), ['+@row2column+'], 120) in ('+@dec_val+') GROUP BY ['+@row2column+'] ORDER BY ['+@row2column+'] ASC  '
PRINT @sql_str
EXEC sp_executesql @sql_str,N'@sql_col_out NVARCHAR(MAX) OUTPUT',@sql_col_out=@sql_col OUTPUT
PRINT @sql_col

SET @sql_str = N'
select a.*,sysp_use_name from (

SELECT * FROM (
    SELECT [pro_dcp_person],[pro_dcp_date],[alldate] FROM [viw_dailypunchcard]) p 
    PIVOT 
    (MAX([alldate]) FOR [pro_dcp_date] IN ( '+ @sql_col+') ) AS pvt
) as a
--自我添加的关联表部分
left join (select * from sysp_user where sysp_use_valid = ''1'') u
on u.sysp_use_id = a.[pro_dcp_person]

ORDER BY a.[pro_dcp_person]
'
PRINT (@sql_str)
EXEC (@sql_str)

输出结果:

行列转换结束……

 

……整了我快六个小时的。一气之下都准备在impl层做数据拆解处理了。

总之方法总比困难多(。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值