将表格中的行转换为一周中的几天

时间:2018-06-26 14:26:24

标签: sql sql-server tsql

我原本认为将是一个相当容易的任务,但变得比我预期的要困难得多。我们有一些任务有时一天执行几次,因此我们有一个表,该表在用户执行任务时添加一行。我需要的是一个月的快照,其中包含执行此任务的人员的姓名缩写和时间:

Snapshot

“活动日志”表非常简单,它只包含执行任务的日期/时间以及执行该任务的用户以及计划的时间(图像中的“通过时间”列);这是我需要整理成一周中几天的表格。

每个“订单”可以有一个或多个“通过时间”,并且每个通过时间可以有当天的零个或多个首字母缩写。例如,对于通过时间8:00,可以在一天中完成几次,或者根本不做。

我尝试使用标准联接来毫无问题地获取订单和计划的通过时间,但是获取一周中的几天可以逃避我的麻烦。我尝试创建一个函数来获取当天的所有首字母缩写,然后创建

'在一周中的每一天都将FuncCall()选择为1,将FuncCall()选择为2',依此类推,但这确实是一个糟糕的表现。

有人知道更好的技术吗?

更新:我认为关于PIVOT的评论看起来很有希望,但不是很确定,因为我发现的所有内容都在PIVOT部分中使用了聚合函数。因此,如果我有下表:

create table #MyTable (OrderName nvarchar(10),DateDone date, TimeDone time, Initials nvarchar(4), PassTime nvarchar(8))

insert into #MyTable values('Order 1','2018/6/1','2:00','ABC','1st Pass')
insert into #MyTable values('Order 1','2018/6/1','2:20','DEF','1st Pass')
insert into #MyTable values('Order 1','2018/6/1','4:40','XYZ','2nd Pass')
insert into #MyTable values('Order 1','2018/6/3','5:00','ABC','1st Pass')
insert into #MyTable values('Order 1','2018/6/4','4:00','QXY','2nd Pass')
insert into #MyTable values('Order 1','2018/6/10','2:00','ABC','1st Pass')

select * from #MyTable

pivot ()  -- Can't figure out what goes here since all examples I see have an aggregate function call such as AVG...

drop table #MyTable

我没有看到如何获得此输出,因为除了聚集首字母之外,我没有聚合其他任何东西: enter image description here

1 个答案:

答案 0 :(得分:3)

像这样吗?

DECLARE @taskTable TABLE(ID INT IDENTITY,Task VARCHAR(100),TaskPerson VARCHAR(100),TaskDate DATETIME);
INSERT INTO @taskTable VALUES
 ('Task before June 2018','AB','2018-05-15T12:00:00')
,('Task 1','AB','2018-06-03T13:00:00')
,('Task 1','CD','2018-06-04T14:00:00')
,('Task 2','AB','2018-06-05T15:00:00')
,('Task 1','CD','2018-06-06T16:00:00')
,('Task 1','EF','2018-06-06T17:00:00')
,('Task 1','EF','2018-06-06T18:00:00')
,('Task 2','GH','2018-06-07T19:00:00')
,('Task 1','CD','2018-06-07T20:00:00')
,('After June 2018','CD','2018-07-15T21:00:00');

SELECT p.*
FROM
(
    SELECT t.Task
          ,ROW_NUMBER() OVER(PARTITION BY t.Task,CAST(t.TaskDate AS DATE) ORDER BY t.TaskDate) AS Taskindex
          ,CONCAT(t.TaskPerson,' ',CONVERT(VARCHAR(5),t.TaskDate,114)) AS Content
          ,DAY(TaskDate) AS ColumnName
    FROM @taskTable t
    WHERE YEAR(t.TaskDate)=2018 AND MONTH(t.TaskDate)=6
) tbl
PIVOT
(
    MAX(Content) FOR ColumnName IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
                                  ,[11],[12],[13],[14],[15],[16],[17],[18],[19],[20]
                                  ,[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31])
) P
ORDER BY P.Task,Taskindex;

结果

+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task   | Taskindex | 1    | 2    | 3        | 4        | 5        | 6        | 7        | 8    | 9    | 10   | 11   | 12   | 13   | 14   | 15   | 16   | 17   | 18   | 19   | 20   | 21   | 22   | 23   | 24   | 25   | 26   | 27   | 28   | 29   | 30   | 31   |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task 1 | 1         | NULL | NULL | AB 13:00 | CD 14:00 | NULL     | CD 16:00 | CD 20:00 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task 1 | 2         | NULL | NULL | NULL     | NULL     | NULL     | EF 17:00 | NULL     | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task 1 | 3         | NULL | NULL | NULL     | NULL     | NULL     | EF 18:00 | NULL     | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task 2 | 1         | NULL | NULL | NULL     | NULL     | AB 15:00 | NULL     | GH 19:00 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+

第一个技巧是使用当天的索引(DAY())作为列名。第二招是ROW_NUMBER()。这将为每个任务和日期添加一个运行索引,从而复制每个索引的行。否则,您每天只会获得一个条目。

您输入的表将更加复杂,但是我认为这显示了原理...

更新:所以我们必须使它更加流畅:-D

WITH prepareData AS
(
    SELECT t.Task
          ,t.TaskPerson
          ,t.TaskDate
          ,CONVERT(VARCHAR(10),t.TaskDate,126) AS TaskDay
          ,DAY(t.TaskDate) AS TaskDayIndex
          ,CONVERT(VARCHAR(5),t.TaskDate,114) AS TimeContent
    FROM @taskTable t
    WHERE YEAR(t.TaskDate)=2018 AND MONTH(t.TaskDate)=6
)
SELECT p.*
FROM
(
    SELECT t.Task
          ,STUFF((
            SELECT ', ' + CONCAT(x.TaskPerson,' ',TimeContent)
            FROM prepareData AS x
            WHERE x.Task=t.Task
              AND x.TaskDay= t.TaskDay
            ORDER BY x.TaskDate
            FOR XML PATH(''),TYPE
           ).value(N'.',N'nvarchar(max)'),1,2,'') AS Content
          ,t.TaskDayIndex
    FROM prepareData t
    GROUP BY t.Task, t.TaskDay,t.TaskDayIndex
) p--tbl
PIVOT
(
    MAX(Content) FOR TaskDayIndex IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
                                    ,[11],[12],[13],[14],[15],[16],[17],[18],[19],[20]
                                    ,[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31])
) P
ORDER BY P.Task;

结果

+--------+------+------+----------+----------+----------+------------------------------+----------+------+
| Task   | 1    | 2    | 3        | 4        | 5        | 6                            | 7        | 8    |
+--------+------+------+----------+----------+----------+------------------------------+----------+------+
| Task 1 | NULL | NULL | AB 13:00 | CD 14:00 | NULL     | CD 16:00, EF 17:00, EF 18:00 | CD 20:00 | NULL |
+--------+------+------+----------+----------+----------+------------------------------+----------+------+
| Task 2 | NULL | NULL | NULL     | NULL     | AB 15:00 | NULL                         | GH 19:00 | NULL |
+--------+------+------+----------+----------+----------+------------------------------+----------+------+

这将在相关子查询中使用经过充分讨论的XML技巧,以便将所有常见条目汇总在一起。使用此统一的内容,您可以走常规的PIVOT路径。聚合不会计算任何内容,因为-肯定-每个像元只有一个值。