查找上个财政月的最后一个星期二之后的第一个星期三

时间:2015-09-23 07:52:58

标签: sql-server tsql date financial

我的一位客户将(财政上的奇怪理由)财务月定义为在一个月的最后一个星期二(包括在内)之后的星期三开始的一段时间,并持续到下个月的最后一个星期二(含)

我需要找到最后一个和当前财政月的开始。

一些例子:

如果今天是2015年9月23日,我需要获得7月29日和8月26日,因为当前的财政月份是从8月26日到9月29日。

如果今天是2015年9月30日,我需要在8月26日到9月30日。

我有不同的客户有不同的定义,这意味着他们中的一些人正在使用星期三而其他人正在使用星期一所以我需要这一天作为参数,如星期一= 1和星期三= 3.我称之为FDOM,FirstDayOfMonth

到目前为止,我的工作重点是使用我在当前和上个月的第一天和最后一天找到的公式,修改后考虑到FDOM。我设法在上个月的上个星期三获得了,但这有时是不正确的,因为我正在考虑属于太阳月份的月份的一天,但也考虑到下个财政月份,如9月30日属于太阳能9月,但属于金融10月,财务十月开始于9月30日。

DECLARE @BASE AS DateTime = '19000101 00:00'

DECLARE @FDOM AS INT = 3 --Wednesday

DECLARE @Datevalue AS DATE = GETDATE()

SET DATEFIRST @FDOM

select DATEADD(D,1-(DATEPART(dw,DATEADD(D,-1,DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @Datevalue) , @BASE)))),DATEADD(D,-1,DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @Datevalue) , @BASE)))

这给了我上个月最后一个星期二之后的第一个星期三,这在9月1日到9月29日(8月26日)是正确的#34;当前财政月的开始"。但是9月30日这应该是错误的,因为它应该在8月26日到8月底也应该在8月26日给出8月26日,而不是7月29日。

4 个答案:

答案 0 :(得分:0)

认为这符合您的要求。它很长但很有希望,通过打破事物和命名事物,我明确了我们如何得出最终答案,所以如果它不太正确,它可以适应:

declare @FDOM int
set @FDOM = 3 --Wednesday. 0 = Sunday, 6 = Saturday

declare @KnownDay datetime
set @KnownDay = DATEADD(day,@FDOM - 1,'20150301') --Offset from a "known good" Sunday to the day before FDOM
declare @EOLastDec datetime
set @EOLastDec = DATEADD(year,DATEDIFF(year,'20010101',GETDATE()),'20001231')
declare @Today datetime
set @Today = DATEADD(day,DATEDIFF(day,0,GETDATE()),0) --You can change this to test other key dates

;With Numbers(n) as (--If you have a numbers table, you can skip this CTE
    select ROW_NUMBER() OVER (ORDER BY so1.object_id) - 1
    from sys.objects so1 cross join sys.objects so2
), LastOfMonths as (
    select DATEADD(month,n,@EOLastDec) as LOM
    from Numbers
    where n between 0 and 13
), LastImportant as (
    select DATEADD(day,-n,LOM) as EOFMonth
    from LastOfMonths cross join Numbers
    where n between 0 and 6 and
    DATEPART(weekday,DATEADD(day,-n,LOM)) = DATEPART(weekday,@KnownDay)
)
select DATEADD(day,1,li0.EOFMonth) as StartOfMonth,DATEADD(day,1,li1.EOFMonth) as EndOfMonth
from
    LastImportant li1
        cross join
    LastImportant li2
        left join
    LastImportant li1_anti
        on
            li1.EOFMonth < li1_anti.EOFMonth and
            li1_anti.EOFMonth <= @Today
        left join
    LastImportant li2_anti
        on
            li2.EOFMonth > li2_anti.EOFMonth and
            li2_anti.EOFMonth >= @Today
        inner join
    LastImportant li0
        on
            li0.EOFMonth < li1.EOFMonth
        left join
    LastImportant li0_anti
        on
            li0_anti.EOFMonth < li1.EOFMonth and
            li0.EOFMonth < li0_anti.EOFMonth
where
    li1.EOFMonth <= @Today and
    li2.EOFMonth >= @Today and
    li1_anti.EOFMonth is null and
    li2_anti.EOFMonth is null and
    li0_anti.EOFMonth is null

希望CTE有合理的解释。我们生成一个数字表,然后我们计算每个月的最后一天,然后我们从那里逐步升级到6天以找到正确类型的一天(即星期二,如果@FDOM是3)

我最初只使用li1li2(以及li1_antili2_anti)进行了更简单的最终查询,但意识到查询只是查找当前的财务月份 - 所以我添加了另外两个联接(使用li0li0_anti)来查找上一个财政月的开头。

答案 1 :(得分:0)

试试这个。您可以使用EOMONTH函数在Sql Server 2012或更高版本上获得月末。

Click to see the fiddle demo.

DECLARE @date DATETIME = GETDATE()
DECLARE @LastMonthEnd DATETIME = DATEADD(day, -1, DATEADD(month, DATEDIFF(month, 0, @date), 0)) 
DECLARE @CurrentMonthEnd DATETIME =  DATEADD(day, -1, DATEADD(month, DATEDIFF(month, 0, @date) + 1, 0)) 


SET DATEFIRST 1

;WITH CTE1 AS
(
    SELECT 1 number, DATEPART(WEEKDAY, @LastMonthEnd) FirstDay, 
                     DATEPART(WEEKDAY, @CurrentMonthEnd) LastDay    
    UNION ALL

    SELECT 1+number,  DATEPART(WEEKDAY, DATEADD(DAY, -number, @LastMonthEnd)),  
                      DATEPART(WEEKDAY, DATEADD(DAY, -number, @CurrentMonthEnd))
    FROM CTE1 
    WHERE number < 7
)
SELECT DATEADD(DAY, -(SELECT Number FROM CTE1 WHERE FirstDay = 3), @LastMonthEnd) StartDate,
       DATEADD(DAY, -(SELECT Number FROM CTE1 WHERE LastDay = 3), @CurrentMonthEnd) EndDate

答案 2 :(得分:0)

计算上个月的开始日期和当月的最后一天,并使用CTE生成它们之间的所有日期。之后,从这两个月获得MAX工作日。

DECLARE @CurrentDate    DATE = '2015-08-23'
DECLARE @StartDate      DATE,
        @EndDate        DATE,
        @MonthEnd       INT = 3

--  Get the first day from previous month and last day from current month
SELECT  @StartDate      = DATEADD(MONTH , DATEDIFF(MONTH, 0, @CurrentDate)-1, 0),
        @EndDate        = DATEADD(SECOND,-1, 
                          DATEADD(MONTH , DATEDIFF(MONTH, 0, @CurrentDate)+1,0))

;WITH   Calendar    AS
(       -- Generate all dates between @StartDate and @EndDate
        SELECT  @StartDate  [Date]
        UNION   ALL
        SELECT  DATEADD(D, +1, Calendar.[Date])
        FROM    Calendar
        WHERE   Calendar.[Date] < @EndDate
)
SELECT  DATEADD(DAY, +1, MAX(StartDate.[Date])) StartDate,
        DATEADD(DAY, +1, MAX(EndDate  .[Date])) EndDate
FROM    Calendar    StartDate,
        Calendar    EndDate
WHERE   -- Get the max weekday from previous month
        DATEPART(MONTH  , StartDate.[Date]) = DATEPART(MONTH, @StartDate) AND
        DATEPART(WEEKDAY, StartDate.[Date]) = @MonthEnd AND
        -- Get the max weekday from current month
        DATEPART(MONTH  , EndDate  .[Date]) = DATEPART(MONTH, @EndDate) AND
        DATEPART(WEEKDAY, EndDate  .[Date]) = @MonthEnd

答案 3 :(得分:0)

经过大量的努力,我设法找到一个不使用CTE的表达,因为我不确定我不能在所有地方使用CTE,我将不得不使用它。

所以基本上我首先理解CASE如果我考虑的日期是它所属月份的最后一个星期三之前或之后。然后我返回两个月和一个月前的最后一个结婚日或者一个月前和本月的最后一个星期三。 这也改变了FDOM,我在今年的几个月进行了测试。它似乎总能奏效。 可能使用EOMonth会缩短它,但我必须验证我可以在我的服务器中使用它。

对不起,我刚才指出要求我不能仅在评论中使用CTE,但感谢您的帮助

DECLARE @datevalue AS Datetime = getdate()
DECLARE @BASE AS DateTime = '19000101 00:00'
DECLARE @FDOM AS INT = 3 --1 is for Monday
SET DATEFIRST @FDOM

SELECT CASE WHEN (@datevalue < DATEADD(D, 1-(DATEPART(dw,DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) + 1, @BASE))),DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) + 1, @BASE)))
        THEN(DATEADD(D, 1-(DATEPART(dw,DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) - 1, @BASE))),DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) - 1, @BASE)))
        ELSE(DATEADD(D, 1-(DATEPART(dw,DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) - 0, @BASE))),DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) - 0, @BASE)))
        END AS [Start of Last Financial Month]
,CASE WHEN (@datevalue < DATEADD(D, 1-(DATEPART(dw,DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) + 1, @BASE))),DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) + 1, @BASE)))
        THEN(DATEADD(D, 1-(DATEPART(dw,DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) + 0, @BASE))),DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) + 0, @BASE)))
        ELSE(DATEADD(D, 1-(DATEPART(dw,DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) + 1, @BASE))),DATEADD(MONTH, DATEDIFF(MONTH, @BASE, @datevalue) + 1, @BASE)))
        END AS [Start of Current Financial Month]