1

在 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,但我不太确定如何执行该操作。有没有更好的方法我没有想到?

4

2 回答 2

1

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

SQL小提琴演示

@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
于 2013-09-25T18:07:33.330 回答
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;
于 2013-09-25T18:12:01.707 回答