查找每位员工的连续工作日期

时间:2013-09-25 17:19:45

标签: sql sql-server sql-server-2000 gaps-and-islands

在SQL 2000中,我有一个包含以下内容的表:

ID Date WorkingTime EmployeeID

8月份,该表格将包含200名员工,日期为8/1 - 8/31。我需要找出每个员工从过去一天开始到后退的连续工作时间前5天的最短日期。

例如: 如果员工123看起来如下并且通过了8/10/2013:

ID Date WorkingTime EmployeeID
1  8/1      1           123 
2  8/2      0           123
3  8/3      0           123
4  8/4      1           123
5  8/5      1           123
6  8/6      1           123
7  8/7      1           123
8  8/8      1           123
9  8/9      0           123
10 8/10     1           123

结果将是8/4。这需要对表中的所有员工一次性完成,因此他们都会有不同的最短日期,所有这些日期都从8/10开始,因为那是传递给查询的日期。这张桌子在现实生活中非常大,并且包含许多日期和员工,而不仅仅是在Auguest。我想过使用游标来完成所有这些但是我认为这会非常慢。我还考虑将所有工作时间添加到临时表并对其进行约会,以找到具有1的日期值的连续5,但我不太确定如何执行它。有没有更好的方式我没想到?

2 个答案:

答案 0 :(得分:1)

下面的查询将为您想要实现的目标提供良好的开端,并根据您的表修改架构。

SQL小提琴demo

@DateToPull - 您要为其提取数据的日期。
 #TimeSheet是你原来的表
 #SubsetTimeSheet - 包含来自#TimeSheet表的记录子集的表。填写了从月初到过去的记录。

注意:使用较新版本的SQL Server可以更有效地编写此查询。

  declare @DateToPull datetime
  select @DateToPull = '08/10/2013'

  if object_id('tempdb..#TimeSheet') is not null
     drop table #TimeSheet

  create table #TimeSheet
  (
     ID int identity(1, 1),
     EmployeeID int,
     [WorkDate] datetime,
     WorkingTime bit
  )

  insert into #TimeSheet(EmployeeID, [WorkDate], WorkingTime)
  select 123 , '08/01/2013', 0
  union all
  select 123 , '08/02/2013', 1
  union all
  select 123 , '08/03/2013', 0
  union all
  select 123 , '08/04/2013', 1
  union all
  select 123 , '08/05/2013', 1
  union all
  select 123 , '08/06/2013', 1
  union all
  select 123 , '08/07/2013', 1
  union all
  select 123 , '08/08/2013', 1
  union all
  select 123 , '08/09/2013', 0
  union all
  select 123 , '08/10/2013', 1
  union all
  select 123 , '08/11/2013', 1
  union all
  select 123 , '08/12/2013', 1
  union all
  select 123 , '08/13/2013', 1
  union all
  select 123 , '08/14/2013', 1
  union all
  select 123 , '08/15/2013', 0
  union all
  select 123 , '08/16/2013', 1
  union all
  select 123 , '08/17/2013', 1
  union all
  select 123 , '08/18/2013', 1
  union all
  select 123 , '08/19/2013', 1
  union all
  select 123 , '08/20/2013', 1

  if object_id('tempdb..#SubsetTimeSheet') is not null
     drop table #SubsetTimeSheet

  create table #SubsetTimeSheet
  (
     EmployeeID int,
     [WorkDate] datetime,
     WorkingTime bit
  )

  insert into #SubsetTimeSheet(EmployeeID, [WorkDate], WorkingTime)
  select EmployeeID, [WorkDate], WorkingTime
  from #TimeSheet
  where
         datediff(dd, [WorkDate], @DateToPull) >= 0
     and datediff(dd, DATEADD(dd, -(DAY(@DateToPull)-1), @DateToPull), [WorkDate]) >= 0
     and WorkingTime = 1
  order by    
     EmployeeID,
     [WorkDate] desc

  select A.EmployeeID, max(E.WorkDate) WorkDate
  from 
     #SubsetTimeSheet A
     inner join #SubsetTimeSheet B on datediff(dd, A.[WorkDate] - 1, B.WorkDate) = 0 and A.EmployeeID = B.EmployeeID
     inner join #SubsetTimeSheet C on datediff(dd, A.[WorkDate] - 2, C.WorkDate) = 0 and A.EmployeeID = C.EmployeeID
     inner join #SubsetTimeSheet D on datediff(dd, A.[WorkDate] - 3, D.WorkDate) = 0 and A.EmployeeID = D.EmployeeID
     inner join #SubsetTimeSheet E on datediff(dd, A.[WorkDate] - 4, E.WorkDate) = 0 and A.EmployeeID = E.EmployeeID
  group by
        A.EmployeeID

答案 1 :(得分:1)

DECLARE @MyTable TABLE
(
    ID      INT IDENTITY PRIMARY KEY,
    [Date]  SMALLDATETIME NOT NULL,
    WorkingTime INT NOT NULL,
    EmployeeID  INT NOT NULL
);
INSERT  @MyTable ([Date], WorkingTime, EmployeeID)
-- First employee
SELECT  '20130801', 1, 123 UNION ALL 
SELECT  '20130802', 0, 123 UNION ALL 
SELECT  '20130803', 0, 123 UNION ALL 
SELECT  '20130804', 1, 123 UNION ALL 
SELECT  '20130805', 1, 123 UNION ALL 
SELECT  '20130806', 1, 123 UNION ALL 
SELECT  '20130807', 1, 123 UNION ALL 
SELECT  '20130808', 1, 123 UNION ALL 
SELECT  '20130809', 0, 123 UNION ALL 
SELECT  '20130810', 1, 123 UNION ALL
-- Second employee
SELECT  '20130801', 1, 126 UNION ALL 
SELECT  '20130802', 1, 126 UNION ALL 
SELECT  '20130803', 1, 126 UNION ALL 
SELECT  '20130804', 1, 126 UNION ALL 
SELECT  '20130805', 1, 126 UNION ALL 
SELECT  '20130806', 0, 126 UNION ALL 
-- Third employee
SELECT  '20130801', 0, 127 UNION ALL 
SELECT  '20130802', 0, 127 UNION ALL 
SELECT  '20130803', 1, 127 UNION ALL 
SELECT  '20130804', 1, 127 UNION ALL 
SELECT  '20130805', 0, 127 UNION ALL 
SELECT  '20130806', 0, 127; 

-

DECLARE @Results TABLE
(
    EmployeeID  INT NOT NULL,
    DaysDiff    INT NOT NULL,
    PRIMARY KEY(EmployeeID, DaysDiff), -- This is a "clustered index"/index organized table
    RowNum      INT IDENTITY NOT NULL,
    [Date]      SMALLDATETIME NOT NULL
);
INSERT  @Results (EmployeeID, DaysDiff, [Date])
SELECT  x.EmployeeID,   
        DATEDIFF(DAY, 0, x.[Date]) AS DaysDiff,
        x.[Date]
FROM    @MyTable x
WHERE   x.WorkingTime = 1
/*
This ORDER BY clause and the clustered index (PRIMARY KEY(EmployeeID, DaysDiff))
should give a hint to SQL Server so that 
RowNum IDENTITY values will be generated in this order: EmployeeID, DaysDiff

Note #1: There is not 100% guarantee that insert order will be the same as 
ORDER BY x.EmployeeID, DaysDiff
and 
clustered index key (EmployeeID, DaysDiff)

Note #2: This INSERT INTO table with identity column simulates the ROW_NUMBER function
which is available starting with SQL2005.
*/
ORDER BY x.EmployeeID, DaysDiff
OPTION (MAXDOP 1); -- It minimizes the risk of messing up the order of RowNum

SELECT  y.EmployeeID, MAX(y.GroupStartDate) AS FirstGroupStartDate
FROM
(
    SELECT  x.EmployeeID, x.GroupID, 
            MIN(x.[Date]) AS GroupStartDate, MAX(x.[Date]) AS GroupEndDate,
            DATEDIFF(DAY, MIN(x.[Date]), MAX(x.[Date]))+1 AS ContinuousDays
    FROM
    (
        SELECT  *, r.DaysDiff - r.RowNum AS GroupID
        FROM    @Results r
    ) x
    GROUP BY x.EmployeeID, x.GroupID
) y
WHERE y.ContinuousDays > 4
GROUP BY y.EmployeeID;