使用 SQL 将月末关闭业务日期分组到一个桶中

Grouping Month End Close Business Dates into a bucket using SQL

我目前正在使用这个逻辑来计算这些月底的结束日期

(定义为当月最后一个工作日、下个月第一个工作日和下个月第二个工作日)

 SELECT             
 CASE 
 WHEN DATE_VALUE IN 
   ( 
     SELECT DATE_VALUE 
           FROM   ( 
                   SELECT   ROW_NUMBER() OVER ( PARTITION BY FIRST_DAY_OF_CALENDAR_MONTH_DATE ORDER BY DAY_NUMBER_IN_MONTH )      AS [MEC1],
                            ROW_NUMBER() OVER ( PARTITION BY FIRST_DAY_OF_CALENDAR_MONTH_DATE ORDER BY DAY_NUMBER_IN_MONTH )      AS [MEC2] ,
                            ROW_NUMBER() OVER ( PARTITION BY FIRST_DAY_OF_CALENDAR_MONTH_DATE ORDER BY DAY_NUMBER_IN_MONTH DESC ) AS [MEC3] ,
                            DATE_VALUE 
                            FROM     DATE_DIM
                            WHERE    RELATIVE_MONTH BETWEEN -13 AND 0
                                     AND      WEEK_DAY_IND = 1 
                                     AND      COMPANY_HOLIDAY_IND = 0 
                          ) d
                          WHERE  MEC1 = 1 -- To get First Day of Business Month
                                 OR MEC2 = 2 -- To get Second Day of Business Month
                                 OR MEC3 = 1 -- To get Last Day of Business Month        
                  ) THEN 1 
                    ELSE 0 
END AS [MEC_IND] 

然后我选择 [MEC_IND] = 1

的日期
 SELECT 
       Date_Value,
       FIRST_DAY_OF_CALENDAR_MONTH_DATE
FROM ...       
WHERE  C.MEC_IND = 1

使用这种逻辑,我设法以这种方式获取日期,

我如何修改逻辑以将这些月末关闭业务日期分组到像这样的桶中

请记住,我使用的是 SQL Server 2008,无法使用 LEAD 和 LAG 功能。

有人可以指导我吗?任何帮助将不胜感激。提前致谢。

我能想到的最直接的方法是使用 dateadd()case 表达式来根据 day() 添加一个或零个月并将结果截断到月初。

在此示例中使用临时日期 table:

declare @fromdate date = '20170428'
declare @thrudate date = '20170704'
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
  select top (datediff(day, @fromdate, @thrudate)+1) 
      [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
  from n as deka cross join n as hecto cross join n as kilo
                cross join n as tenK cross join n as hundredK
   order by [Date]
)
select 
    date = [Date]
  , EffectiveMonth = dateadd(month
        , datediff(month, 0
          , dateadd(month,case when day(date) > 27 then 1 else 0 end, [Date])
        )
      , 0)
from dates
where day(date) <4 or day(date) > 27; -- for brevity in results

rextester 演示:http://rextester.com/HJXXYL94271

returns:

+------------+----------------+
|    date    | EffectiveMonth |
+------------+----------------+
| 2017-04-28 | 2017-05-01     |
| 2017-04-29 | 2017-05-01     |
| 2017-04-30 | 2017-05-01     |
| 2017-05-01 | 2017-05-01     |
| 2017-05-02 | 2017-05-01     |
| 2017-05-03 | 2017-05-01     |
| 2017-05-28 | 2017-06-01     |
| 2017-05-29 | 2017-06-01     |
| 2017-05-30 | 2017-06-01     |
| 2017-05-31 | 2017-06-01     |
| 2017-06-01 | 2017-06-01     |
| 2017-06-02 | 2017-06-01     |
| 2017-06-03 | 2017-06-01     |
| 2017-06-28 | 2017-07-01     |
| 2017-06-29 | 2017-07-01     |
| 2017-06-30 | 2017-07-01     |
| 2017-07-01 | 2017-07-01     |
| 2017-07-02 | 2017-07-01     |
| 2017-07-03 | 2017-07-01     |
+------------+----------------+


要分解表达式,组合的主要两个部分是 dateadd(),它使用 case 表达式来确定是否应添加月份(对于该月的最后一个工作日)(每月的第一个工作日)和日期截断。

带有case表达式的dateadd()是:

dateadd(month
  , case when day(date) > 27 then 1 else 0 end
  , [Date])


将日期截断到该月的第一天很简单,但语法可能不直观。将自 1900-01-01 以来的月数添加到日期 1900-01-01 可以让我们将日期截断到月初。

例如,对于当前日期:

select dateadd(month, datediff(month, 0, getdate() ), 0)

同一表达式的注释版本:

select dateadd(month 
      , datediff(month
        , 0 /* '19000101' */
        , getdate() /* date */
        ) /* end of datediff, returns integer number of months */ 
      , 0 /* '19000101' */
      )


将日期截断与我们用 case 表达式导出的日期组合起来,结果如下:

select dateadd(month 
      , datediff(month
        , 0 /* '19000101' */
        , dateadd(month
          , case when day([Date]) > 27 then 1 else 0 end
          , [Date]) /* date */
      ) /* end of datediff, returns integer number of months */ 
  , 0 /* '19000101' */
  )