【问题标题】:Calculate working days - Monday to Fri in Tsql在 Sql 中计算工作日 - 星期一到星期五
【发布时间】:2011-10-12 19:26:06
【问题描述】:

我如何计算周一至周五的最后工作五天。我当前的脚本获得了上周一的日期,但我无法获得上周五的日期。请帮忙

declare @StartDate datetime
declare @EndDate datetime


--Calculate date range for report

select @EndDate = Cast(convert(char(10), getdate(), 101)+' 00:00:00' as datetime)
select @StartDate = DateAdd(d, -7, @EndDate)
select @EndDate = Cast(convert(char(10), getdate(), 101)+' 23:59:59' as datetime)

select @StartDate startdate
select @EndDate  enddate

【问题讨论】:

  • @Brian Dishaw 是的,也许看起来像那个问题,但给定的代码不是很好,答案没有被接受。顺便说一句 Faiz,6 个问题,您还没有接受任何答案

标签: tsql sql-server-2008


【解决方案1】:

Datepart 为您提供了一种获取工作日的简单方法:

 SET DATEFIRST 1  -- monday is first day of the week
 SELECT DATEPART(weekday, '20110725')
 -- result is 1

请参阅T-SQL Date functionsSET DATEFIRST 了解更多信息。

使用工作日,您可以算出上周一和周五有多少天,并使用“AddDate”(就像您现在所做的那样)来计算。

请注意,您确实应该使用DATEDIFF 进行此类日期范围选择。如果您选择 23:59:59 之前的所有内容,则总有可能会遗漏一些记录。例如 23:59:59.001 超出范围但仍在同一天。使用DATEDIFF,您可以测试是否在同一天,丢弃时间部分。无需费心转换为字符串,添加时间和转换回。

【讨论】:

    【解决方案2】:

    答案比人们想象的要复杂。您需要返回 5 天,然后找到之前的第一个星期一,以及之后的第一个星期五。您可以为此使用@@datefirst、计算或“设置firstdate 1”。我不喜欢使用最后一个,因为它不能在函数中完成。如您所见,我使用了计算,@@datefirst 一样好。

    假设您想要过去的星期一到星期五的最后一组。这个查询会得到那个。如果你相信你当前的星期一,你可以只增加 5 天并减去 1 分钟(我不相信它,如果你在星期一运行查询,它只会返回上星期一)。

    在我的sql中,我的目标不是简单,而是效率。

    DECLARE @getdate datetime = dateadd(day, cast(getdate() as int), 0)
    -- the 'Declare' can also be written like this thanks to @Andriy M
    --DECLARE @getdate = CAST(GETDATE() - 0.5 AS int)
    SELECT @getdate - 5 - CAST(@getdate- 5 as int) % 7 monday,
    dateadd(minute, -1, @getdate) - CAST(@getdate- 5 as int) % 7 friday
    

    结果:

    Monday              Friday
    2011-07-18 0:00:00  2011-07-22 23:59:00
    

    *第一个解决方案是休息一天@AndriyM 指出,它已经解决了。


    回答@Andriy M

    由于某种原因,它的行为与我预期的不同。我无法解释,但试试这个

    select cast(dateadd(day, cast(getdate() as int) - .5, 0) as datetime),
    cast(dateadd(day, cast(getdate() as int), 0) as datetime),
    cast(dateadd(day, cast(getdate() as int) + .5, 0) as datetime)
    

    早上最后两个字段的值相同,晚上前两个字段的值相同。我和你一样惊讶,我希望我能解释一下。在这里测试过 https://data.stackexchange.com/stackoverflow/query/new

    【讨论】:

    • 你没有在cast(getdate() as int)中使用- 0.5,为什么?你知道cast(datetime as int) rounds 日期时间而不是截断它,不是吗?
    • 我的意思是cast(cast('20110725 18:00:00' as datetime) as int) 的返回值与cast(cast('20110725 06:00:00' as datetime) as int) 不同。
    • @Andriy M 答案是我的脚本
    • 当我第一次了解到这一点时,我确实感到很惊讶,但说实话,从那以后已经有几个月了。事实是,正如我上面所说,cast(datetime as int) 不仅截断时间部分,而且将 datetime 值四舍五入到最接近的日期,或者是同一个(如果它的时间是早上),或者是下一个(如果时间是下午)。换句话说,cast(datetime as int) 等价于cast(round(cast(datetime as float), 0) as int)。 …
    • 上面的DECLARE @getdate 语句可以这样实现:DECLARE @getdate = CAST(GETDATE() - 0.5 AS int)(如果您打算用GETDATE() 的日期部分初始化@getdate)。即,您从datetime 中减去半天,如果是在下午,则变为上午,CAST 会生成一个int,相当于GETDATE() 的同一日期。如果它最初是在早上,那么在减去它之后就变成了前一天的下午,CAST 再次将它四舍五入到等于GETDATE() 的实际日期。 …
    【解决方案3】:

    这两个问题:

    可能会有所帮助。

    不过,如果您已经知道如何找到最后一个星期一,则可以通过使用 DATEADD() 函数在星期一日期上加上 4 天来轻松找到对应的星期五。例如:

    SELECT @EndDate = DATEADD(DAY, 4, @StartDate)
    

    【讨论】:

      【解决方案4】:

      这一切都与今天的日期有关,对吧?

      所以找出今天是什么DATEPART(weekday, getdate())

      然后把它变成一个调整变量——例如:

      declare @adjustment int
      set @adjustment = CASE DATEPART(weekday, getdate()) WHEN 'MONDAY' THEN 0 WHEN 'TUESDAY' THEN 1, ...END
      

      那么你想要的星期一就是今天 - 7 - @adjustment
      ...而您想要的星期五是星期一 + 5

      Declare @myMonday smalldatetime,
              @myFriday smalldatetime
      set @myMonday = getdate() - 7 - @adjustment
      set @myFriday = @myMonday + 5
      

      【讨论】:

        【解决方案5】:
        DECLARE @my int
        DECLARE @myDeduct int
        DECLARE @day INT
        DECLARE @mydate DATETIME
        
        SET @mydate = '2012-08-01'
        
        SET @myDeduct = 0
        SET DateFirst 1 -- Set it monday=1 (value)
        
        --Saturday and Sunday on the first and last day of a month will Deduct 1
        IF (DATEPART(weekday,(DATEADD(dd,-(DAY(@mydate)-1),@mydate))) > 5)
        SET @myDeduct = @myDeduct + 1
        
        IF (DATEPART(weekday,(DATEADD(dd,-(DAY(DATEADD(mm,1,@mydate))),DATEADD(mm,1,@mydate)))) > 5)
        SET @myDeduct = @myDeduct + 1
        
        SET @my = day(DATEADD(dd,-(DAY(DATEADD(mm,1,@mydate))),DATEADD(mm,1,@mydate)))
        
        select (((@my/7) * 5 + (@my%7)) - @myDeduct) as Working_Day_per_month
        

        【讨论】:

        • 虽然这确实产生了一个有趣的结果,有人有兴趣知道,但它实际上并没有回答上面提出的问题。再看看上面.. ;-)
        【解决方案6】:

        我也会把我的帽子扔进擂台。 :-)

        DECLARE @Date      datetime = '01/11/2015'
        DECLARE @StartDate datetime = DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date - 6) % 7)), @Date - 6) -- MONDAY
        DECLARE @EndDate   datetime = DATEADD(d, (5 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date - 6) % 7)), @Date - 6) -- FRIDAY
        
              SELECT '@Date'      as Variable ,CONVERT(date, @Date)      as DateValue ,DATENAME(dw, @Date)      as DayOfTheWeek
        UNION SELECT '@StartDate' as Variable ,CONVERT(date, @StartDate) as DateValue ,DATENAME(dw, @StartDate) as DayOfTheWeek
        UNION SELECT '@EndDate'   as Variable ,CONVERT(date, @EndDate)   as DateValue ,DATENAME(dw, @EndDate)   as DayOfTheWeek
        
        -- Variable    DateValue   DayOfTheWeek
        -- ----------  ----------  ------------
        -- @Date       2015-01-11  Sunday
        -- @StartDate  2015-01-05  Monday
        -- @EndDate    2015-01-09  Friday
        

        奖励:在这里,您可以使用相同的技术生成 5 个工作日的快速表格。

        SELECT DATENAME(dw, DATEADD(d, TT.DaysToAdd, DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date - 6) % 7)), @Date - 6))) as DayOfTheWeek
              ,             DATEADD(d, TT.DaysToAdd, DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date - 6) % 7)), @Date - 6))  as DateValue
          FROM (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM (VALUES(0),(0),(0),(0),(0)) a(n)) as TT
        
        -- DayOfTheWeek                   DateValue
        -- ------------------------------ -----------------------
        -- Monday                         2015-01-05 00:00:00.000
        -- Tuesday                        2015-01-06 00:00:00.000
        -- Wednesday                      2015-01-07 00:00:00.000
        -- Thursday                       2015-01-08 00:00:00.000
        -- Friday                         2015-01-09 00:00:00.000
        

        这里有一个解释:

        1) 首先我们需要知道从什么日期开始我们的评估。对于此示例,我们选择使用 2015 年 1 月 11 日星期日。

        DECLARE @Date2 datetime = '01/11/2015'
        

        1b) 给定日期值,这是一种很好的获得星期几名称的奖励技巧

        SELECT @Date2 as DateValue, DATENAME(dw, @Date2) as DayOfTheWeek
        

        2) 接下来,我们需要确定性地(基于美国日历)知道给定的星期几在数字上介于 1 和 7 之间

        • 注意:1899.12.31 是 1900.01.01 之前的第一个星期日,这是 SmallDateTime 数据类型的最小值。
        • 注意:是的,您可以像这样更简单地使用 DATEPART(dw, @Date),但鉴于某些服务器环境可能有不同的配置,它不是确定性的
        • 结果:1​​ = 星期日 | 2 = 星期一 | 3 = 星期二 | 4 = 星期三 | 5 = 星期四 | 6 = 星期五 | 7 = 星期六

          SELECT ((DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date2) % 7) + 1) [DayOfWeek Deterministic (Based on US)]
          

        3) 现在,给定任何日期,您应该有一种确定性的方式来确定该周的星期一

        SELECT DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date2) % 7)), @Date2) as [Monday Day of the Week - Deterministic (Based on US)]
        

        4) 现在,给定任何日期,您应该有一种确定性的方法来确定给定周的星期五

        SELECT DATEADD(d, (5 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date2) % 7)), @Date2) as [Monday Day of the Week - Deterministic (Based on US)]
        

        5) 我们需要的最后一个日期操作技术是知道如何进入一个工作日的第一个完整周发生在它之前的一周。例如,如果我们在星期天并从中减去 1 天,那么我们就到了星期六,这使我们处于前一周,即工作日的第一个完整周。或者,如果我们也从星期一减去 1 天,它只会让我们到星期日,而不是前一周,所以减去 1 天是不够的。另一方面,如果我们是在星期六并减去 7 天,我们会超过工作日的前一周,进入前一周,这太远了。这是一个分析的总结,以找出可以减去的神奇数字是什么,这将适用于一周中的任何一天。如下所示,幻数是 6。

        --                                            DAYS TO SUBTRACT
        -- Day of the Week      - 0     - 1     - 2     - 3     - 4     - 5     - 6     - 7
        -- ===============      ====    ====    ====    ====    ====    ====    ====    ====
        -- Sunday               Bad     Good    Good    Good    Good    Good    Good    Good
        -- Monday               Bad     Bad     Good    Good    Good    Good    Good    Good
        -- Tuesday              Bad     Bad     Bad     Good    Good    Good    Good    Good
        -- Wednesday            Bad     Bad     Bad     Bad     Good    Good    Good    Good
        -- Thursday             Bad     Bad     Bad     Bad     Bad     Good    Good    Good
        -- Friday               Bad     Bad     Bad     Bad     Bad     Bad     Good    Good
        -- Saturday             Good    Good    Good    Good    Good    Good    Good    Bad
        

        BONUS)如果您想将所有工作日都放在小表中,那么您还需要使用基于零的快速“计数表”。有很多方法可以做到这一点,所以选择你的口味。这里有几个。

        SELECT * FROM (SELECT 0 as DaysToAdd UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) as TT
        SELECT * FROM (SELECT TOP 5 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM sys.all_columns a CROSS JOIN sys.all_columns b) as TT
        SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM (VALUES(0),(0),(0),(0),(0)) a(n)) as TT
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-08-24
          • 1970-01-01
          • 2012-01-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多