【问题标题】:How to get date of a beginning of first work weekday even if the Monday is from last month?即使星期一是从上个月开始,如何获取第一个工作日的开始日期?
【发布时间】:2020-03-04 13:55:09
【问题描述】:

我的目标是获取将在一个月内返回工作日的查询。我可以获得一个月中的日期,但我需要从星期一到星期五获取日期,即使星期一可能在上个月。 示例 4 月 1 日是星期三,所以我需要将 3 月 30 日和 31 日带回来。最后返回的日期是 5 月 1 日,因为那是最后一个星期五,其中包含一些 4 月的日子。

【问题讨论】:

  • 这是应用程序的一部分吗?使用代码而不是 SQL 查询会更容易实现。
  • SQL 可以很容易地实现这一点,@silkfire。这里的关键是 OP 有一个 Calendar 表。
  • 不幸的是,它将成为存储过程的一部分,作为 SSRS 报告的数据集。它将填写当月和接下来2个月的员工时间表
  • 好吧,我相信 SQL 是用于存储,而不是用于复杂的编程逻辑 :)
  • @silkfire 我同意 sql 不适用于复杂的编程逻辑,但日历表不是复杂的编程逻辑。它实际上是存储日期以及这些日期的属性。

标签: sql sql-server ssrs-2012


【解决方案1】:

如果对辅助功能感兴趣,我有生成日历的 TVF。

示例

Select * from [dbo].[tvf-Date-Calendar-Wide]('2020-04-01')

退货

所以,稍微调整一下,我们就可以了

Select WeekNr = RowNr
      ,B.*
 From  [dbo].[tvf-Date-Calendar-Wide]('2020-04-01') A
 Cross Apply ( values (Mon)
                     ,(Tue)
                     ,(Wed)
                     ,(Thu)
                     ,(Fri)
             ) B(Date)

哪个返回

WeekNr  Date
1       2020-03-30
1       2020-03-31
1       2020-04-01
1       2020-04-02
1       2020-04-03
2       2020-04-06
2       2020-04-07
2       2020-04-08
...
5       2020-04-29
5       2020-04-30
5       2020-05-01

感兴趣的函数

CREATE FUNCTION [dbo].[tvf-Date-Calendar-Wide] (@Date1 Date)
Returns Table
Return (

Select RowNr,[Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]
 From  (
        Select D
              ,DOW=left(datename(WEEKDAY,d),3)
              ,RowNr = sum(Flg) over (order by D)
         From (
                Select D,Flg=case when datename(WEEKDAY,d)= 'Sunday' then 1 else 0 end
                 From (Select Top (42) D=DateAdd(DAY,-7+Row_Number() Over (Order By (Select Null)),@Date1) From  master..spt_values n1 ) A
              ) A
       ) src
 Pivot (max(d) for DOW in ([Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]) )pvg
 Where [Sun] is not null
   and [Sat] is not null
 )
-- Select * from [dbo].[tvf-Date-Calendar-Wide]('2020-04-01')

【讨论】:

    【解决方案2】:

    您首先需要找到该月第一天的一周开始日期,然后是包含该月最后一天的一周结束日期:

    例如

    SELECT  WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, '20200401')-1), '20200401'),
            WeekEnd = DATEADD(DAY, 7-(DATEPART(WEEKDAY, '20200430')), '20200430');
    

    给予:

    WeekStart       WeekEnd
    ------------------------------
    2020-03-29      2020-05-02
    

    您不想硬编码每月的第一天和最后一天,但这些都是从日期中获得的相当微不足道的东西:

    DECLARE @Date DATE = '20200415';
    
    SELECT  MonthStart = DATEADD(MONTH, DATEDIFF(MONTH, 0, @Date), 0),
            MonthEnd = EOMONTH(@Date);
    

    返回

    MonthStart      MonthEnd
    ------------------------------
    2020-04-01      2020-04-30
    

    然后,您可以将其替换为第一个查询周开始:

    DECLARE @Date DATE = '20200401';
    
    SELECT  WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, DATEADD(MONTH, DATEDIFF(MONTH, 0, @Date), 0))-1), DATEADD(MONTH, DATEDIFF(MONTH, 0, @Date), 0)),
            WeekEnd = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(@Date))), EOMONTH(@Date));
    

    这给出了与第一个带有硬编码日期的查询相同的输出。不过这很笨拙,所以我将把它分成更进一步的步骤:

    DECLARE @Date DATE = '20200401';
    
    -- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
    SET @Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, @Date), 0);
    
    SELECT  WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, @Date)-1), @Date),
            Weekend = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(@Date))), EOMONTH(@Date));
    

    同样,这给出了相同的输出(2020-03-29 和 2020-05-02)。

    下一步是填写工作日之间的所有日期。如果你有calendar table,这很简单

    DECLARE @Date DATE = '20200415';
    
    -- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
    SET @Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, @Date), 0);
    
    DECLARE @Start DATE = DATEADD(DAY, -(DATEPART(WEEKDAY, @Date)-1), @Date),
            @End DATE = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(@Date))), EOMONTH(@Date));
    
    SELECT  [Date], DayName = DATENAME(WEEKDAY, [Date])
    FROM    Calendar
    WHERE   Date >= @Start
    AND     Date <= @End
    AND     IsWeekday = 1
    ORDER BY [Date];
    

    如果您没有日历表,那么我建议您创建一个,但如果您无法创建一个,您仍然可以通过generating a set series numbers 即时生成它,并将这些数字添加到您的开始日期:

    DECLARE @Date DATE = '20200415';
    
    -- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
    SET @Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, @Date), 0);
    
    DECLARE @Start DATE = DATEADD(DAY, -(DATEPART(WEEKDAY, @Date)-1), @Date),
            @End DATE = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(@Date))), EOMONTH(@Date));
    
    -- GET NUMBERS FROM 0 - 50
    WITH Dates (Date) AS
    (   SELECT  TOP (DATEDIFF(DAY, @Start, @End)) 
                DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY n1.n) - 1, @Start)
        FROM    (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n1 (n)
                CROSS JOIN (VALUES (1),(1),(1),(1),(1)) n2 (n)
    )
    SELECT  [Date], DayName = DATENAME(WEEKDAY, [Date])
    FROM    Dates
    WHERE   ((DATEPART(WEEKDAY, [Date]) + @@DATEFIRST) % 7) NOT IN (0, 1);
    

    【讨论】:

      【解决方案3】:

      只需生成所有可能的日期 - 最多可在月份开始前 6 天。取第一个星期一之后的有效工作日:

      with dates as (
            select dateadd(day, -6, convert(date, '2020-04-01')) as dte
            union all
            select dateadd(day, 1, dte)
            from dates
            where dte < '2020-04-30'
           )
      select dte
      from (select d.*,
                   min(case when datename(weekday, dte) = 'Monday' then dte end) over () as first_monday
            from dates d
           ) d
      where datename(weekday, dte) not in ('Saturday', 'Sunday') and
            dte >= first_monday;
      

      【讨论】:

        【解决方案4】:
        declare @dateVal datetime = GETDATE(); --assign your date here
        
        declare @monthFirstDate datetime = cast(YEAR(@dateVal) as varchar(4)) + '-' + DATENAME(mm, @dateVal) + '-' + cast(01 as varchar(2))
        declare @monthLastDate datetime = DAteADD(day, -1, DATEADD(month, 1, cast(YEAR(@dateVal) as varchar(4)) + '-' + DATENAME(mm, @dateVal) + '-' + cast(01 as varchar(2))))
        
        declare @startDate datetime = DATEADD(DAY, 2 - DATEPART(WEEKDAY, @monthFirstDate), CAST(@monthFirstDate AS DATE)) 
        declare @enddate datetime = DATEADD(DAY, 6 - DATEPART(WEEKDAY, @monthLastDate), CAST(@monthLastDate AS DATE)) 
        
        Select @startDate StartDate, @enddate EndDate
        
        ****Result**
        --------------------------------------------------------------
            StartDate                |   EndDate
        -----------------------------|--------------------------------
            2020-03-02 00:00:00.000  |  2020-04-03 00:00:00.000
        -----------------------------|---------------------------------**
        

        【讨论】:

          猜你喜欢
          • 2020-07-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-01-12
          • 1970-01-01
          相关资源
          最近更新 更多