【问题标题】:SQL Server query to get list of 2nd last working day of all months in a date range which includes holidays from a calenderSQL Server查询以获取日期范围内所有月份的第二个最后一个工作日的列表,其中包括日历中的假期
【发布时间】:2018-07-02 00:52:31
【问题描述】:

我需要编写一个查询来获取:

  1. 日期结果集。结果集包含从当前日期前 3 个月开始的所有日期 + 当前日期后 12 个月(包括当前月份)的所有日期。例如:说当前日期:7/25/2018 结果集应包含从 2018 年 4 月 1 日到 2019 年 6 月 30 日的所有日期

  2. 从此结果集中,将获得每个月的倒数第二个工作日。

条件:倒数第二个工作日应为平日和非节假日。假期列表包含在名为RT_STATEHOLIDAY_MV 的表中。

RT_STATEHOLIDAY_MV:

Description
-----------
01/02/17
01/16/17
02/28/17
04/27/18
05/29/17
07/04/17
09/04/17
11/10/17
11/23/17
12/25/17

到目前为止我所尝试的:

WITH cte AS 
(
    SELECT 
        1 AS DayID,
        DATEADD(month, -3, DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)) AS FromDate,
        DATENAME(dw, DATEADD(month, -3, DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0))) AS Dayname
    UNION ALL
    SELECT 
        cte.DayID + 1 AS DayID,
        DATEADD(d, 1, cte.FromDate),
        DATENAME(dw, DATEADD(d, 1, cte.FromDate)) AS Dayname
    FROM 
        cte
    WHERE 
        DATEADD(d, 1, cte.FromDate) < DATEADD(month, 12, DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0))
) ,
wrkInd as 
(
    SELECT 
        (SELECT CONVERT(VARCHAR(10), CTE.FromDate, 1) AS [MMDDYY]) AS Date, 
        CASE 
           WHEN REFSH.DESCRIPTION IS NULL THEN 'Y' ELSE 'N' END AS WORKDAY_INDICATOR
    FROM 
        CTE
    LEFT JOIN 
        RT_STATEHOLIDAY_MV REFSH ON CTE.FromDate =  REFSH.DESCRIPTION
    WHERE 
        DayName NOT IN ('Saturday','Sunday')
),
secLastWrkDay AS
(
    SELECT 
        *,
        CASE WHEN wrkInd.Date = 
--(SELECT MAX(wrkInd.Date) 
--from wrkInd where wrkInd.Date <=
(select case 
    datename(WEEKDAY,EOMONTH(wrkInd.Date)) 
    when 'Saturday' then convert(datetime,(EOMONTH(wrkInd.Date)))-2
    when 'Sunday' then  convert(datetime, (EOMONTH(wrkInd.Date)))-3 
    when 'Monday' then convert(datetime, (EOMONTH(wrkInd.Date)))-3 
    else  convert(datetime,EOMONTH(wrkInd.Date))-1 
end) 
-- AND wrkInd.WORKDAY_INDICATOR='Y'
-- AND MONTH(tab)=MONTH(wrkInd.Date)
--)
THEN 'Y' ELSE 'N' END AS SECOND_LAST_WRK_DAY
FROM wrkInd
),
cutoff AS
(
    SELECT 
        secLastWrkDay.Date, secLastWrkDay.WORKDAY_INDICATOR,
        CASE 
           WHEN MONTH(secLastWrkDay.Date) = MONTH(EOMONTH(secLastWrkDay.Date))
                AND YEAR(secLastWrkDay.Date) = YEAR(EOMONTH(secLastWrkDay.Date))
                AND secLastWrkDay.WORKDAY_INDICATOR = 'Y'
                AND secLastWrkDay.SECOND_LAST_WRK_DAY = 'Y'
               THEN 'Y' 
               ELSE 'N' 
        END AS SECOND_LAST_WORK_DAY
    FROM 
        secLastWrkDay
)
SELECT * 
FROM cutoff
OPTION (MaxRecursion 700);

这可行,但这并没有考虑假期表中的假期。

【问题讨论】:

  • 关于日历、节假日、工作日和非工作日的讨论一直在发生。只需在互联网上搜索有关使用所需信息创建真实日历表的建议(并注意您对工作日的假设 - 自然和非自然灾害无处不在)。并吸取过去的教训。没有人 - NO ONE - 应该使用 2 位数年份的日期。

标签: sql-server


【解决方案1】:

问题是您要根据工作日减去 1、2 或 3 天,而不考虑节假日。 顺便说一句,您正在过滤掉周末,因此 secLastWrkDay 中的“周六”和“周日”案例将永远不会发生。 您可以使用每年/每月(和 WORKDAY_INDICATOR)剩余天数的反向编号,并选择第 2 个工作日作为您想要的工作日。另外,我建议不要将 varchar 用于日期值。您的查询可能如下所示:

WITH 
  cte (FromDate) AS (
    SELECT DATEADD(month, DATEDIFF(month, 0, GETDATE()) - 3, 0)
    UNION ALL
    SELECT DATEADD(d, 1, cte.FromDate)
    FROM cte
    WHERE DATEADD(d, 1, cte.FromDate) < DATEADD(month, DATEDIFF(month, 0, GETDATE()) + 12, 0)
  ),
  wrkInd ([Date], WORKDAY_INDICATOR) AS (
    SELECT cte.FromDate, 
      CASE WHEN REFSH.DESCRIPTION IS NULL THEN 'Y' ELSE 'N' END
    FROM CTE
      LEFT JOIN RT_STATEHOLIDAY_MV REFSH ON CTE.FromDate =  REFSH.DESCRIPTION
    WHERE DATENAME(dw, cte.FromDate) NOT IN ('Saturday','Sunday')
  ),
  dayNumber ([Date], WORKDAY_INDICATOR, dayNumber) AS (
    SELECT [Date], WORKDAY_INDICATOR
      , ROW_NUMBER() OVER 
        (PARTITION BY YEAR([Date]), MONTH([Date]), WORKDAY_INDICATOR ORDER BY [Date] DESC)
    FROM wrkInd
  ),
  cutoff ([Date], WORKDAY_INDICATOR, SECOND_LAST_WORK_DAY) AS (
    SELECT [Date], WORKDAY_INDICATOR
      , CASE 
          WHEN WORKDAY_INDICATOR = 'Y' AND dayNumber = 2
          THEN 'Y' ELSE 'N' 
        END
    FROM dayNumber
  )
SELECT * FROM cutoff ORDER BY [Date]
OPTION (MaxRecursion 700);

【讨论】:

    【解决方案2】:

    看看这个。如果我能正确理解你

    WITH CTE AS
    (
    SELECT Cast(DATEADD(month, -3, GETDATE()) AS DATE) AS D
    UNION ALL 
    SELECT
    DATEADD(day, 1, C.D) AS D
    FROM
    CTE C
    WHERE
    C.D<Cast(DATEADD(month, 11, EOMONTH(GETDATE())) AS DATE)  
    )
    SELECT
    C.D
    FROM
    CTE C
    LEFT JOIN RT_STATEHOLIDAY_MV H ON H.D=C.D
    WHERE
    datename(WEEKDAY,C.D) NOT IN ('Saturday', 'Sunday')
    AND H.D IS NULL
    OPTION (MaxRecursion 1000);
    

    关于小提琴http://sqlfiddle.com/#!18/52aff/24/0的示例

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-25
      • 2012-02-26
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多