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

时间:2020-09-01 16:26:44

标签: sql sql-server ssms sql-server-2016 transpose

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

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的一些建议。

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

3 个答案:

答案 0 :(得分:1)

说明

如果您不想使用动态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',  '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,'HHmm','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

答案 1 :(得分:1)

如何?

设置

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 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')

查询

  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,
        CASE WHEN DATEPART(dw,ShiftDate) = 2 THEN FORMAT(SHIFTSTart, 'HHmm') + '-' + FORMAT(ShiftEnd, 'HHmm') ELSE '' END AS Monday,
        CASE WHEN DATEPART(dw,ShiftDate) = 3 THEN FORMAT(SHIFTSTart, 'HHmm') + '-' + FORMAT(ShiftEnd, 'HHmm') ELSE '' END AS Tuesday,
        CASE WHEN DATEPART(dw,ShiftDate) = 4 THEN FORMAT(SHIFTSTart, 'HHmm') + '-' + FORMAT(ShiftEnd, 'HHmm') ELSE '' END AS Wednesday,
        CASE WHEN DATEPART(dw,ShiftDate) = 5 THEN FORMAT(SHIFTSTart, 'HHmm') + '-' + FORMAT(ShiftEnd, 'HHmm') ELSE '' END AS Thursday,
        CASE WHEN DATEPART(dw,ShiftDate) = 6 THEN FORMAT(SHIFTSTart, 'HHmm') + '-' + FORMAT(ShiftEnd, 'HHmm') ELSE '' END AS Friday,
        CASE WHEN DATEPART(dw,ShiftDate) = 7 THEN FORMAT(SHIFTSTart, 'HHmm') + '-' + FORMAT(ShiftEnd, '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

答案 2 :(得分:1)

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

数据

CREATE TABLE test_TABLE
(
    ShiftID  INT,
    PeopleID  INT, 
    ShiftDate Date,
    ShiftStart DATETIME,
    ShiftEnd DATETIME
)
INSERT INTO test_TABLE
VALUES
(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');

查询

declare @userdate   DATE='2020-08-25';
declare @wk_int     int=datediff(wk, 0, @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,
       isnull(max(case when datediff(d, @wk_start, t.ShiftDate)=1 then tm.hrs else null end), 'OFF') as Monday,
       isnull(max(case when datediff(d, @wk_start, t.ShiftDate)=2 then tm.hrs else null end), 'OFF') as Tuesday,
       isnull(max(case when datediff(d, @wk_start, t.ShiftDate)=3 then tm.hrs else null end), 'OFF') as Wednesday,
       isnull(max(case when datediff(d, @wk_start, t.ShiftDate)=4 then tm.hrs else null end), 'OFF') as Thursday,
       isnull(max(case when datediff(d, @wk_start, t.ShiftDate)=5 then tm.hrs else null end), 'OFF')as Friday,
       isnull(max(case when datediff(d, @wk_start, 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)), ':', ''),
                         replace(convert(char(5),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