【问题标题】:Find consecutive working dates for each employee查找每个员工的连续工作日期
【发布时间】:2013-09-25 17:19:44
【问题描述】:

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

ID Date WorkingTime EmployeeID

对于 8 月,此表将包含 200 名员工,日期为 8 月 1 日至 8 月 31 日。我需要找出每个员工前 5 天连续工作时间的 MIN 日期,从传入之日开始,然后往后计算。

例如: 如果员工 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 开始,因为那是传递到查询中的日期。这张表在现实生活中非常大,包含许多日期和员工,而不仅仅是八月。我考虑过使用光标来完成所有这些操作,但我认为那会很慢。我还考虑将所有工作时间添加到临时表并对其进行 datediff 以找到 datediff 为 1 的连续 5,但我不太确定如何执行该操作。有没有更好的方法我没有想到?

【问题讨论】:

  • 真的在使用 SQL Server 2000? :(
  • 寻找 WorkingTime = 5 的总和可能是解决方案的一部分。
  • @Goat CO,告诉我。
  • 嘿,SQL Server 2000 非常棒……在当时。

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


【解决方案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;

【讨论】:

    【解决方案2】:

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

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-05
      • 1970-01-01
      • 2013-01-09
      • 1970-01-01
      • 2021-09-24
      • 2013-05-02
      • 2016-09-17
      相关资源
      最近更新 更多