如何将行中的某些值转换为列?

问题描述

我有一个表,用于存储人们的轮班信息。其结构为:

SHIFT_TABLE
-----------
ShiftID  PeopleID  ShiftDate   ShiftStart         ShiftEnd
1        41        2020-08-24  2020-08-24 06:00   2020-08-24 14:00
2        41        2020-08-26  2020-08-26 06:00   2020-08-26 14:00
3        41        2020-08-27  2020-08-27 19:00   2020-08-28 03:00
4        41        2020-08-29  2020-08-29 10:00   2020-08-29 16:00
5        58        2020-08-24  2020-08-24 14:00   2020-08-24 21:30
6        58        2020-08-25  2020-08-25 14:00   2020-08-25 23:00
7        58        2020-08-30  2020-08-30 08:00   2020-08-30 18:00

Columns     Data types
-------     ----------
ShiftDate   date
ShiftStart  datetime
ShiftEnd    datetime

用户将只发送一个日期(无时间),新的存储过程将返回该周(从星期日到星期六)所有人员的轮班信息。因此,如果use从一个星期中选择任何日期,例如星期一或星期三,结果将完全相同。

例如,如果用户2020-08-25发送给SP,则将返回除ShiftID为7的行以外的所有行。

发送2020-08-25时,结果应如下所示:

Person  2020-08-23  2020-08-24   2020-08-25   2020-08-26   2020-08-27   2020-08-28  2020-08-29
41      OFF         06:00-14:00  OFF          06:00-14:00  19:00-03:00  OFF         10:00-16:00
58      OFF         14:00-21:30  14:00-21:30  OFF          OFF          OFF         OFF

不用日期作为列名,我可以使用日期名,因为这种方法可以避免使用动态sql

Person  Sunday  Monday       Tuesday      Wednesday    Thursday     Friday  Saturday
41      OFF     06:00-14:00  OFF          06:00-14:00  19:00-03:00  OFF     10:00-16:00
58      OFF     14:00-21:30  14:00-21:30  OFF          OFF          OFF     OFF

我想避免使用动态sql或XML solutions。我可以获得工作日编号(1代表星期日,2代表星期一,依此类推)。我不确定这是否会有所帮助。有没有一种方法可以将ShiftStartShiftEnd列与PoepleID换位。我已经可以用ShiftDate过滤结果集。列名将每周更改一次。我想不惜一切代价避免使用动态sql的一些建议。

也许有一些使用数据透视的方法,但这似乎是一种较慢的方法

解决方法

说明

如果您不想使用动态sql,并且可以使用“星期一,星期二...星期日” (或每天等值的数字1 =星期一,2 =星期二)就可以了。 ..)作为列标题,而不是像“ 2020.08.23 2020.08.24 2020.08.25 2020.08.26 2020.08.27 2020.08.28 2020.08.29” 之类的实际日期值,则可以实现您的要求:>

查询

declare @WorkDate date = '2020.08.25',@StartFilter date,@EndFilter date

declare @shift_table table ( ShiftID int identity(1,1),PeopleID int,ShiftDate varchar(max),ShiftStart datetime,ShiftEnd datetime )
insert into @shift_table (PeopleID,ShiftDate,ShiftStart,ShiftEnd )
      select 41,'2020.08.24','2020-08-24 06:00','2020-08-24 14:00'
union select 41,'2020.08.26','2020-08-26 06:00','2020-08-26 14:00'
union select 41,'2020.08.27','2020-08-27 19:00','2020-08-28 03:00'
union select 41,'2020.08.29','2020-08-29 10:00','2020-08-29 16:00'
union select 58,'2020-08-24 14:00','2020-08-24 21:30'
union select 58,'2020.08.25','2020-08-25 14:00','2020-08-25 23:00'
union select 58,'2020.08.30','2020-08-30 08:00','2020-08-30 18:00'

-- get the week start and end date filters based on the @WorkDate passed in
SELECT @StartFilter =  DATEADD(dd,-(DATEPART(dw,@WorkDate)-1),@WorkDate) /* week start */,@EndFilter = DATEADD(dd,7-(DATEPART(dw,@WorkDate)),@WorkDate) /* week end */

select
    Person,min(Monday) as [Monday],min(Tuesday) as [Tuesday],min(Wednesday) as [Wednesday],min(Thursday) as [Thursday],min(Friday) as [Friday],min(Saturday) as [Saturday],min(Sunday) as [Sunday]
from (
    select
        PeopleID as Person,isnull([Monday],'OFF') as [Monday],isnull([Tuesday],'OFF') as [Tuesday],isnull([Wednesday],'OFF') as [Wednesday],isnull([Thursday],'OFF') as [Thursday],isnull([Friday],'OFF') as [Friday],isnull([Saturday],'OFF') as [Saturday],isnull([Sunday],'OFF') as [Sunday]
    from (
    select
        *,DATENAME(dw,cast(ShiftDate as date)) [Day],format(ShiftStart,'HHmm','en-us') + '-' + format(ShiftEnd,'en-us') ShiftTime
    from @shift_table
    where cast(ShiftDate as date) between @StartFilter and @EndFilter
    ) src
    pivot
    (
        max(ShiftTime)
        for [Day] in ([Monday],[Tuesday],[Wednesday],[Thursday],[Friday],[Saturday],[Sunday])
    ) piv
) P
group by P.Person

结果

enter image description here

,

如何?

设置

CREATE TABLE SHIFT_TABLE
(
    ShiftID  INT,PeopleID  INT,ShiftDate Date,ShiftStart DATETIME,ShiftEnd DATETIME
)
INSERT INTO SHIFT_Table
VALUES
(1,41,'2020-08-24','2020-08-24 14:00'),(2,'2020-08-26','2020-08-26 14:00'),(3,'2020-08-27','2020-08-28 03:00'),(4,'2020-08-29','2020-08-29 16:00'),(5,58,'2020-08-24 21:30'),(6,'2020-08-25','2020-08-25 23:00'),(7,'2020-08-30','2020-08-30 18:00')

查询

  DECLARE @userdate DATE = '2020-08-25'
  


SELECT PeopleID,CASE WHEN MAX(Sunday) = '' THEN 'OFF' ELSE Max(Sunday) END AS Sunday,CASE WHEN MAX(Monday) = '' THEN 'OFF' ELSE Max(Monday) END AS Monday,CASE WHEN MAX(Tuesday) = '' THEN 'OFF' ELSE Max(Tuesday) END AS Tuesday,CASE WHEN MAX(Wednesday) = '' THEN 'OFF' ELSE Max(Wednesday) END AS Wednesday,CASE WHEN MAX(Thursday) = '' THEN 'OFF' ELSE Max(Thursday) END AS Thursday,CASE WHEN MAX(Friday) = '' THEN 'OFF' ELSE Max(Friday) END AS Friday,CASE WHEN MAX(Saturday) = '' THEN 'OFF' ELSE Max(Saturday) END AS Saturday
FROM 
(
    SELECT 
        PeopleId,CASE WHEN DATEPART(dw,ShiftDate) = 1 THEN FORMAT(SHIFTSTart,'HHmm') + '-' + FORMAT(ShiftEnd,'HHmm') ELSE '' END AS Sunday,ShiftDate) = 2 THEN FORMAT(SHIFTSTart,'HHmm') ELSE '' END AS Monday,ShiftDate) = 3 THEN FORMAT(SHIFTSTart,'HHmm') ELSE '' END AS Tuesday,ShiftDate) = 4 THEN FORMAT(SHIFTSTart,'HHmm') ELSE '' END AS Wednesday,ShiftDate) = 5 THEN FORMAT(SHIFTSTart,'HHmm') ELSE '' END AS Thursday,ShiftDate) = 6 THEN FORMAT(SHIFTSTart,'HHmm') ELSE '' END AS Friday,ShiftDate) = 7 THEN FORMAT(SHIFTSTart,'HHmm') ELSE '' END AS Saturday
    FROM SHIFT_TABLE
    WHERE DATEPART(Week,ShiftDate) = DATEPART(Week,@UserDate)
) s
GROUP BY PeopleID

结果

PeopleID    Sunday  Monday    Tuesday   Wednesday   Thursday    Friday Saturday
41          OFF     0600-1400 OFF       0600-1400   1900-0300   OFF    1000-1600
58          OFF     1400-2130 1400-2300 OFF         OFF         OFF    OFF
,

此方法将@userdate转换为wk_start日期,并使用它创建有条件聚合的数据透视表。

数据

CREATE TABLE test_TABLE
(
    ShiftID  INT,ShiftEnd DATETIME
)
INSERT INTO test_TABLE
VALUES
(1,'2020-08-30 18:00');

查询

declare @userdate   DATE='2020-08-25';
declare @wk_int     int=datediff(wk,@userdate);
declare @wk_start   date=dateadd(d,-1,dateadd(wk,@wk_int,0)); 


SELECT PeopleID,isnull(max(case when datediff(d,@wk_start,t.ShiftDate)=0 then tm.hrs else null end),'OFF') as Sunday,t.ShiftDate)=1 then tm.hrs else null end),'OFF') as Monday,t.ShiftDate)=2 then tm.hrs else null end),'OFF') as Tuesday,t.ShiftDate)=3 then tm.hrs else null end),'OFF') as Wednesday,t.ShiftDate)=4 then tm.hrs else null end),'OFF') as Thursday,t.ShiftDate)=5 then tm.hrs else null end),'OFF')as Friday,t.ShiftDate)=6 then tm.hrs else null end),'OFF') as Saturday
FROM
  test_TABLE t
  cross apply
  (select concat_ws('-',replace(convert(char(5),cast(ShiftStart as time)),':',''),cast(ShiftEnd as time)),'')) hrs) tm
where ShiftDate>=@wk_start 
      and ShiftDate<=dateadd(d,7,@wk_start)
group by
  peopleid;

结果

PeopleID    Sunday  Monday      Tuesday     Wednesday   Thursday    Friday  Saturday
41          OFF     0600-1400   OFF         0600-1400   1900-0300   OFF     1000-1600
58          OFF     1400-2130   1400-2300   OFF         OFF         OFF      OFF