计算每周工作时间并使用SQL排除周末

时间:2016-07-22 17:09:36

标签: sql sql-server tsql

我有如下所示的表格,我想在每周的所有5周工作日内捕获员工是否工作。如果员工只工作3天,那么我想显示他/她工作的3天的工作时间,并为2个缺失的日子分配0:

UID        DT         HOURS_WORKED
Mike      07/4/16          5
Mike      07/5/16          8
Mike      07/7/16          4

这是此方案的预期结果

UID        DT         HOURS_WORKED
Mike      07/4/16          5
Mike      07/5/16          8
Mike      06/6/16          0
Mike      07/7/16          4
Mike      07/8/16          0

所以当他们跳过当天的工作时我想把0。我不想显示周末。谢谢你的帮助

select UID, DT, HOURS_WORKED from my table

2 个答案:

答案 0 :(得分:1)

构建一个日期cte,其中包含一周内的所有日期,并在此表上执行外部联接,以便在员工在给定工作日不工作时显示0

with x as (select cast('2016-07-01' as date) dt
           union all
           select dateadd(dd,1,dt) from x where dt < '2016-07-31')
select e.uid, t.dt, coalesce(e.hours_worked,0) hours_worked
from (select * from x where datepart(dw,dt) between 2 and 6) t
left join emp_table e on e.dt = t.dt

答案 1 :(得分:1)

我增强了@vkp的答案,使其更通用,(如果你真的很挑剔我们需要处理边缘情况,在第一周或上周的某些天可能会落入不同年​​份)

在这里,我添加了使用Datefirst设置更改一周的第一天的功能。更多关于Datefirst MSDN:https://msdn.microsoft.com/en-us/library/ms181598.aspx

/*  Setup Test Data
Drop Table #T1 

Create Table #T1 ( UID Varchar(10), Dt   Date, Hrs int )

insert into #T1 Values 
( 'Mike' , GetDate()   , 8 ) ,    -- Sat 07/23
( 'Mike' , GetDate()-1 , 8 ) ,
( 'Mike' , GetDate()-2 , 8 ) ,
( 'Mike' , GetDate()+3 , 8 )      -- Tue 07/26
( 'John' , GetDate()   , 8 ) ,    -- Sat 07/23
( 'John' , GetDate()-1 , 8 ) ,
( 'John' , GetDate()-2 , 8 ) ,
( 'John' , GetDate()+3 , 8 ) 


 insert into #T1 Values 
( 'Mike' , GetDate() - 206   , 8 ) ,   --- One Date for Last Year 12/30 to Test Edge Case

-- select * , DatePart( WEEK, Dt) from #T1
*/

- 创建帮助电视功能以获取一年中某一周的日期

ALTER FUNCTION GetDates
(
    @WK int ,
    @yr varchar(5) = ''
)


RETURNS 
    @Table_Var TABLE 
    (
        DD int, 
        Dt Date,
        Wk int
    )
AS

BEGIN

IF @yr = ''  SET @yr  = YEAR(Getdate()) -- If Year is Blank then Default to current year

Declare @LastDateOfYr Date = RTRIM(@YR)+'-12-31' -- Last Day of the year
Declare @LastDayOfYr Int = CAST(Datename(dy, @LastDateOfYr ) as int) -- No.of Days in the Year to Handle Leap Yrs

;WITH Dates as 
(
--  SELECT 0 as DD          ,  DATEADD(D, 0, @yr )        as Dt  , DatePart( WEEK,  DATEADD(D, 0 , @yr )) as Wk
    SELECT Datepart( DAYOFYEAR,DateAdd(D, (@WK-2)*7, @yr) ) as DD , DATEADD(D, (@WK-2)*7, @yr )  as Dt  ,  @WK-2 as Wk -- Initial values for the Recursive CTE.
    UNION ALL
    SELECT Dates.DD+1 as DD ,  DATEADD(D, Dates.DD, @yr )         , DatePart( WEEK,DATEADD(D, Dates.DD, @yr )) from Dates where Dates.DD <= @LastDayOfYr
    AND Wk <= @WK + 1  -- Terminator for Recursion
) 

INSERT INTO @Table_Var 
SELECT 
        DD , 
        Dt ,
        Wk 
FROM Dates as A 
WHERE A.Wk = @WK
OPTION (MAXRECURSION 21)  -- At any point we dont use CTE For more than 3 Weeks (one week actually). If the CTE is changed by using the commented out Initializer inside the CTE Above then this number has to change accordingly

  RETURN 

END
GO

查询:

SET DATEFIRST 1  -- Make Monday as First Day of Week. The default is Sunday.

Select B.* , A.* , ISNULL(T.Hrs,0) Hours_Worked
FROM
        ( SELECT Distinct UID, 
                DatePart( WEEK, Dt) as WK , 
                DatePart( YEAR, Dt) as Yr 
          FROM #T1  
        ) A 
CROSS APPLY dbo.GetDates(A.WK, A.Yr ) B -- Helper Function Used to apply 
LEFT OUTER JOIN #T1 T ON B.Dt = T.Dt