SQL Query将开始和结束时间转换为正确的日期

时间:2017-09-22 11:21:23

标签: sql sql-server-2012 transpose

我有一个数据源,其员工按以下格式刷入/输出

+----------+---------+-----------+------------+-------+-----------+
| Forename | Surname | Clock_Num | Date       | Time  | Direction |
+----------+---------+-----------+------------+-------+-----------+
| John     | Kerry   | 0111      | 2017-09-21 | 18:00 | IN        |
+----------+---------+-----------+------------+-------+-----------+
| John     | Kerry   | 0111      | 2017-09-22 | 02:00 | OUT       |
+----------+---------+-----------+------------+-------+-----------+
| Bill     | Long    | 0112      | 2017-09-21 | 10:00 | IN        |
+----------+---------+-----------+------------+-------+-----------+
| Bill     | Long    | 0112      | 2017-09-21 | 18:00 | OUT       |
+----------+---------+-----------+------------+-------+-----------+
| George   | Takai   | 0113      | 2017-09-22 | 11:00 | IN        |
+----------+---------+-----------+------------+-------+-----------+

现在我希望看到这些基于转换开始时间几乎转换为每位员工一条记录

+----------+---------+-----------+------------+------------------+------------------+
| Forename | Surname | Clock_Num | Shift Date | Time In          | Time Out         |
+----------+---------+-----------+------------+------------------+------------------+
| John     | Kerry   | 0111      | 2017-09-21 | 2017-09-21 18:00 | 2017-09-22 02:00 |
+----------+---------+-----------+------------+------------------+------------------+
| Bill     | Long    | 0112      | 2017-09-22 | 2017-09-22 10:00 | 2017-09-22 18:00 |
+----------+---------+-----------+------------+------------------+------------------+
| George   | Takai   | 0113      | 2017-09-22 | 2017-09-22 11:00 | Null             |
+----------+---------+-----------+------------+------------------+------------------+

让我难倒的部分是查询的逻辑。

即如果没有时钟输出时间大于单日的时钟输入时间,则向前看下一个OUT,但只要它在下一个IN之前(如果员工忘记退出时),则显示为空

任何人都可以帮助或指出我正确的方向吗?

我接受可能没有一个简单的2-3行答案,因为大量的记录需要相对有效

1 个答案:

答案 0 :(得分:2)

使用common table expressionlead()窗口函数获取Next_Timecross apply()以获取Time之后的Time'out' {1}}'in',但在'{

}中Next_Time'之前

;with cte as (
  select Forename, Surname, Clock_Num, Direction, Date
    , Time = convert(datetime,t.Date)+convert(datetime,t.Time)
    , Next_Time = lead(convert(datetime,t.Date)+convert(datetime,t.Time))
        over (partition by Forename, Surname, Clock_Num, Direction
              order by Date, Time)
  from t
)
select t.Forename, t.Surname, t.Clock_Num, t.Date
  , Time_In  = t.Time
  , Time_Out = x.Time
from cte as t
  outer apply (
    select top 1
        o.Time
    from cte as o
    where o.Forename = t.Forename
      and o.Surname = t.Surname
      and o.Clock_Num = t.Clock_Num
      and o.Direction = 'OUT'
      and o.Time > t.Time
      and (o.Time < t.Next_Time or t.Next_Time is null)
      order by o.Time
  ) as x
where t.Direction = 'IN'
order by clock_num

rextester演示:http://rextester.com/UIMJ36919

返回:

+----------+---------+-----------+------------+---------------------+---------------------+
| Forename | Surname | Clock_Num |    Date    |       Time_In       |      Time_Out       |
+----------+---------+-----------+------------+---------------------+---------------------+
| John     | Kerry   |       111 | 2017-09-21 | 2017-09-21 18:00:00 | 2017-09-22 02:00:00 |
| Bill     | Long    |       112 | 2017-09-21 | 2017-09-21 10:00:00 | 2017-09-21 18:00:00 |
| George   | Takai   |       113 | 2017-09-22 | 2017-09-22 11:00:00 | NULL                |
+----------+---------+-----------+------------+---------------------+---------------------+

对于2008年,您可以使用outer apply()来模拟lead(),如下所示:

;with cte as (
  select Forename, Surname, Clock_Num, Direction, Date
    , Time = convert(datetime,t.Date)+convert(datetime,t.Time)
    , x.Next_Time 
  from t
    outer apply (
      select top 1 
        Next_Time = convert(datetime,o.Date)+convert(datetime,o.Time)
      from t as o
      where o.Forename = t.Forename
        and o.Surname = t.Surname
        and o.Clock_Num = t.Clock_Num
        and o.Direction = t.Direction
        and ((o.Date = t.Date and o.Time > t.Time)
          or o.Date > t.Date)
      order by o.Date, o.Time
  ) as x
)
select t.Forename, t.Surname, t.Clock_Num, t.Date
  , Time_In  = t.Time
  , Time_Out = x.Time
from cte as t
  outer apply (
    select top 1
        o.Time
    from cte as o
    where o.Forename = t.Forename
      and o.Surname = t.Surname
      and o.Clock_Num = t.Clock_Num
      and o.Direction = 'OUT'
      and o.Time > t.Time
      and (o.Time < t.Next_Time or t.Next_Time is null)
      order by o.Time
  ) as x
where t.Direction = 'IN'
order by clock_num

rextester演示:http://rextester.com/LEYS1651