我的一位客户将(财政上的奇怪理由)财务月定义为在一个月的最后一个星期二(包括在内)之后的星期三开始的一段时间,并持续到下个月的最后一个星期二(含)
我需要找到最后一个和当前财政月的开始。
一些例子:
如果今天是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日。
答案 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)
我最初只使用li1
和li2
(以及li1_anti
和li2_anti
)进行了更简单的最终查询,但意识到查询只是查找当前的财务月份 - 所以我添加了另外两个联接(使用li0
和li0_anti
)来查找上一个财政月的开头。
答案 1 :(得分:0)
试试这个。您可以使用EOMONTH函数在Sql Server 2012或更高版本上获得月末。
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]