删除SQL Server中单个表中时间戳列不同的行

时间:2016-12-28 20:23:11

标签: sql sql-server sql-server-2008

我有一张员工时钟冲击表,看起来像这样:

| EmployeeID | PunchDate  | PunchTime | PunchType | Sequence |
|------------|------------|-----------|-----------|----------|
|       5386 | 12/27/2016 | 03:57:42  | On Duty   |      552 |
|       5386 | 12/27/2016 | 09:30:00  | Off Duty  |      563 |
|       5386 | 12/27/2016 | 010:02:00 | On Duty   |      564 |
|       5386 | 12/27/2016 | 12:10:00  | Off Duty  |      570 |
|       5386 | 12/27/2016 | 12:22:00  | On Duty   |      571 |
|       5386 | 12/27/2016 | 05:13:32  | Off Duty  |      578 |  

我需要做的是删除 Off Duty 打卡与以下 值班打卡之间差异的任何行不到25分钟。在上面的例子中,我想删除序列570和571。

我已经通过从另一个表中拉出所有 Off Duty 拳击并使用此查询拉出关闭之后的所有 On Duty 拳击来创建此表职责打孔:

SELECT *  FROM [dbo].[Punches]
INSERT INTO [dbo].[UpdatePunches (EmployeeID,PunchDate,PunchTime,PunchType,Sequence)
SELECT *  FROM [dbo].[Punches]
WHERE Sequence IN (
SELECT Sequence + 1
FROM [dbo].[Punches]
WHERE PunchType LIKE 'Off Duty%') AND
PunchType LIKE 'On Duty%'   

我一直试图在这段代码中加入某种DATEDIFF查询,并作为一个单独的步骤去除这些,但没有任何运气。我不能使用特定的序列号,因为每次打卡都会改变。

我正在使用SQL Server 2008。

我们非常感谢任何建议。

4 个答案:

答案 0 :(得分:2)

您可以根据punchdate和punchtime为每位员工分配rownumbers,并根据日期和时间的升序将每一行与下一行连接。

此后,获取差异小于25分钟的那些行的rownumbers,最后删除这些行。

with rownums as 
(select t.*,row_number() over(partition by employeeid 
                              order by cast(punchdate +' '+punchtime as datetime) ) as rn
 from t)
,rownums_to_delete as 
(
 select r1.rn,r1.employeeid
 from rownums r1
 join rownums r2 on r1.employeeid=r2.employeeid and r1.rn=r2.rn+1
 where dateadd(minute,25,cast(r2.punchdate +' '+r2.punchtime as datetime)) > cast(r1.punchdate +' '+r1.punchtime as datetime)
 and r1.punchtype <> r2.punchtype
 union all
 select r2.rn, r2.employeeid
 from rownums r1
 join rownums r2 on r1.employeeid=r2.employeeid and r1.rn=r2.rn+1
 where dateadd(minute,25,cast(r2.punchdate +' '+r2.punchtime as datetime)) > cast(r1.punchdate +' '+r1.punchtime as datetime)
 and r1.punchtype <> r2.punchtype
)
delete r
from rownums_to_delete rd
join rownums r on rd.employeeid=r.employeeid and r.rn=rd.rn

Sample Demo

如果日期和时间列不是varchar,而是实际datetime数据类型,请在查询中使用punchdate+punchtime

编辑:查询的简单版本是

with todelete as (
select t1.employeeid,cast(t2.punchdate+' '+t2.punchtime as datetime) as punchtime,
t2.punchtype,t2.sequence,
cast(t1.punchdate+' '+t1.punchtime as datetime) next_punchtime, 
t1.punchtype as next_punchtype,t1.sequence as next_sequence
from t t1
join t t2 on t1.employeeid=t2.employeeid 
and cast(t2.punchdate+' '+t2.punchtime as datetime) between dateadd(minute,-25,cast(t1.punchdate+' '+t1.punchtime as datetime)) and cast(t1.punchdate+' '+t1.punchtime as datetime) 
where t2.punchtype <> t1.punchtype
    )
delete t
from t 
join todelete td on t.employeeid = td.employeeid 
and cast(t.punchdate+' '+t.punchtime as datetime) in (td.punchtime,td.next_punchtime)
;

答案 1 :(得分:2)

SQL Server有一个很好的称为可更新CTE的能力。使用lead()lag(),您可以完全按照自己的意愿行事。以下假设日期实际存储为datetime - 这只是为了方便一起添加日期和时间(您还可以明确使用转换):

with todelete as (
      select tcp.*,
             (punchdate + punchtime) as punchdatetime.
             lead(punchtype) over (partition by employeeid order by punchdate, punchtime) as next_punchtype,
             lag(punchtype) over (partition by employeeid order by punchdate, punchtime) as prev_punchtype,
             lead(punchdate + punchtime) over (partition by employeeid order by punchdate, punchtime) as next_punchdatetime,
             lag(punchdate + punchtime) over (partition by employeeid order by punchdate, punchtime) as prev_punchdatetime
      from timeclockpunches tcp
     )
delete from todelete
    where (punchtype = 'Off Duty' and
           next_punchtype = 'On Duty' and
           punchdatetime > dateadd(minute, -25, next_punchdatetime)
          ) or
          (punchtype = 'On Duty' and
           prev_punchtype = 'Off Duty' and
           prev_punchdatetime > dateadd(minute, -25, punchdatetime)
          );

编辑:

在SQL Server 2008中,您可以使用相同的想法,但效率不高:

delete t
    from t outer apply
         (select top 1 tprev.*
          from t tprev
          where tprev.employeeid = t.employeeid and
                (tprev.punchdate < t.punchdate or
                 (tprev.punchdate = t.punchdate and tprev.punchtime < t.punchtime)
                )
          order by tprev.punchdate desc, tprev.punchtime desc
         ) tprev outer apply
         (select top 1 tnext.*
          from t tnext
          where tnext.employeeid = t.employeeid and
                (t.punchdate < tnext.punchdate or
                 (t.punchdate = tnext.punchdate and t.punchtime < tnext.punchtime)
                )
          order by tnext.punchdate desc, tnext.punchtime desc
         ) tnext
where (t.punchtype = 'Off Duty' and
       tnext.punchtype = 'On Duty' and
       t.punchdatetime > dateadd(minute, -25, tnext.punchdatetime)
      ) or
      (t.punchtype = 'On Duty' and
       tprev.punchtype = 'Off Duty' and
       tprev.punchdatetime > dateadd(minute, -25, t.punchdatetime)
      );

答案 2 :(得分:1)

也许这样的东西很容易在那里拍打..这只是使用一个子查询找到下一个值班的&#39;打卡并在主要查询中将其与“关税”进行比较。冲。

          Delete
          FROM [dbo].[Punches] p
          where p.PunchTime >=
          dateadd(minute, -25, isnull (
   (select top 1 p2.PunchTime from [dbo].[Punches] p2 where 
   p2.EmployeeID=p.EmployeeID and p2.PunchType='On Duty' 
   and p1.Sequence < p2.Sequence and p2.PunchDate=p.PunchDate
   order by p2.Sequence asc)
   ),'2500-01-01')
          and p.PunchType='Off Duty'

答案 3 :(得分:1)

您可以从CTE中的日期和时间字段创建日期时间,然后在关机时间之后查找下一个值班时间,如下所示:

;
WITH OnDutyDateTime AS
(
    SELECT 
    EmployeeID,
    Sequence,
    DutyDateTime = DATEADD(ms, DATEDIFF(ms, '00:00:00', PunchTime), CONVERT(DATETIME, PunchDate)) 
    FROM
    #TempEmployeeData 
    where PunchType = 'On Duty'
),
OffDutyDateTime As
(
    SELECT 
    EmployeeID,
    Sequence,
    DutyDateTime = DATEADD(ms, DATEDIFF(ms, '00:00:00', PunchTime), CONVERT(DATETIME, PunchDate)) 
    FROM
    #TempEmployeeData 
    where PunchType = 'Off Duty'
)

SELECT 
    OffDutyDateTime = DutyDateTime,
    OnDutyDateTime = (SELECT TOP 1 DutyDateTime FROM OnDutyDateTime WHERE EmployeeID = A.EmployeeID AND Sequence > A.Sequence ORDER BY Sequence ASC ),
    DiffInMinutes = DATEDIFF(minute,DutyDateTime,(SELECT TOP 1 DutyDateTime FROM OnDutyDateTime WHERE EmployeeID = A.EmployeeID AND Sequence > A.Sequence ORDER BY Sequence ASC ))

FROM
OffDutyDateTime A


OffDutyDateTime         OnDutyDateTime          DiffInMinutes
----------------------- ----------------------- -------------
2016-12-27 09:30:00.000 2016-12-27 10:02:00.000 32
2016-12-27 12:10:00.000 2016-12-27 12:22:00.000 12
2016-12-28 05:13:32.000 NULL                    NULL

(3行受影响)