查找上个财政月最后一个星期二之后的第一个星期三
Find the First Wednesday after the last Tuesday of last financial month
我的一位客户(出于奇怪的财务原因)将财务月定义为 从一个月的最后一个星期二(含)之后的星期三开始并持续到最后一个星期二的时间段次月(含).
我需要找到上一个和当前财务月份的开始。
一些示例:
如果今天是 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 月 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 日)这将是正确的 "the beginning of the current financial month"。但是 9 月 30 日是错误的,因为它应该给出 9 月 30 日,从 8 月 26 日到 8 月底也是错误的,因为它应该给出 8 月 26 日,而不是 7 月 29 日。
我认为这符合您的要求。它很长,但希望通过分解和命名,我可以清楚地说明我们是如何得出最终答案的,因此如果它不太正确,可以进行调整:
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 具有合理的解释性。我们生成一个数字 table,然后我们计算每个月的最后一天,然后从那里开始,我们最多向后退 6 天以找到正确类型的一天(即星期二,如果 @FDOM 是 3 )
我最初有一个更简单的最终查询,只使用 li1
和 li2
(以及 li1_anti
和 li2_anti
),但意识到查询只是查找当前财务月 - 所以我添加了另外两个连接(使用 li0
和 li0_anti
)来查找上一个财务月的开始。
试试这个。您可以使用 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
计算上个月的开始日期和当月的最后一天,并使用 CTE 生成它们之间的所有日期。稍后,获取两个月的最大工作日。
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
经过大量努力,我设法找到了一个不使用 CTE 的表达式,因为我不确定我不能在我必须使用它的所有地方使用 CTE。
所以基本上首先我了解一个案例,如果我正在考虑的日期是在它所属的月份的最后一个星期三之前或之后。然后我 return 两个月前的最后一个星期三或一个月前和这个月的最后一个星期三。
这也可以改变 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]
我的一位客户(出于奇怪的财务原因)将财务月定义为 从一个月的最后一个星期二(含)之后的星期三开始并持续到最后一个星期二的时间段次月(含).
我需要找到上一个和当前财务月份的开始。
一些示例:
如果今天是 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 月 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 日)这将是正确的 "the beginning of the current financial month"。但是 9 月 30 日是错误的,因为它应该给出 9 月 30 日,从 8 月 26 日到 8 月底也是错误的,因为它应该给出 8 月 26 日,而不是 7 月 29 日。
我认为这符合您的要求。它很长,但希望通过分解和命名,我可以清楚地说明我们是如何得出最终答案的,因此如果它不太正确,可以进行调整:
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 具有合理的解释性。我们生成一个数字 table,然后我们计算每个月的最后一天,然后从那里开始,我们最多向后退 6 天以找到正确类型的一天(即星期二,如果 @FDOM 是 3 )
我最初有一个更简单的最终查询,只使用 li1
和 li2
(以及 li1_anti
和 li2_anti
),但意识到查询只是查找当前财务月 - 所以我添加了另外两个连接(使用 li0
和 li0_anti
)来查找上一个财务月的开始。
试试这个。您可以使用 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
计算上个月的开始日期和当月的最后一天,并使用 CTE 生成它们之间的所有日期。稍后,获取两个月的最大工作日。
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
经过大量努力,我设法找到了一个不使用 CTE 的表达式,因为我不确定我不能在我必须使用它的所有地方使用 CTE。
所以基本上首先我了解一个案例,如果我正在考虑的日期是在它所属的月份的最后一个星期三之前或之后。然后我 return 两个月前的最后一个星期三或一个月前和这个月的最后一个星期三。 这也可以改变 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]