我正在尝试创建一个会计年度,其中会计年度从7月1日开始,一周定义为周一到周日。但是例如;如果一个月中一周的第一天是星期六,那么星期六到星期日将在该月被视为1周,新星期从星期一开始,到星期日结束,依此类推。
请参阅下面要创建的表格示例:
我认为我需要一个程序,可能需要在会计年度的第一天,然后迭代一年中的所有日子,添加列的开始和结束日期,周数,当天所属的年份和年份。
答案 0 :(得分:1)
又一种选择。这将产生50年是0.703秒
示例强>
Set DateFirst 1
Declare @Date1 date = '2017-07-01'
Declare @Date2 date = '2019-06-30'
Select Period = Dense_Rank() over (Partition By FY Order By FM)
,Week = Dense_Rank() over (Partition By FY,FM Order By FW)
,StartDate = Min(D) over (Partition By FY,FM,FW )
,EndDate = Max(D) over (Partition By FY,FM,FW )
,DayOfWeek = D
,Year = FY
From (
Select FY = DatePart(Year,@Date1)-1+sum(case when convert(varchar(5),@Date1,101)=convert(varchar(5),D,101) then 1 else 0 end) over (Order By D)
,FM = sum(case when DatePart(Day,D)=DatePart(Day,@Date1) then 1 else 0 end) over (Order By D)
,FW = sum(case when DatePart(WeekDay,D)=1 then 1 else 0 end) over (Order By D)
,D
From (
Select Top (DateDiff(DAY,@Date1,@Date2)+1)
D = DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),@Date1)
From master..spt_values n1,master..spt_values n2
) A1
) A
Order By D
答案 1 :(得分:0)
您不需要程序,但某些功能可以很方便。
首先,请记住,某些日期时间函数依赖于@@ DATEFIRST。要使代码独立于此设置,您可以使用一个标准化日期编号的功能。
CREATE FUNCTION getNormalizedWeekDay
(
@inputDate DATE
, @dateFirst TINYINT = 1
)
RETURNS SMALLINT
AS
BEGIN
RETURN ((DATEPART(WEEKDAY, @inputDate) + @@DATEFIRST - 1 - @dateFirst) % 7) + 1;
END
在此,您可以确定要使用的日期,并根据该结果计算结果。例如,确定一天是星期天是很方便的。
如果我理解正确,你想要一个与内置函数类似的周函数,但将7月1日视为一年的第一天。
这样的事情应该这样做:
CREATE FUNCTION getFiscalWeekNumber
(
@inputDate DATE
, @firstFiscalMonth TINYINT = 7
, @dateFirst TINYINT = 1
)
RETURNS TINYINT
AS
BEGIN
/* NULL Input handling. */
IF (@inputDate IS NULL) BEGIN
RETURN NULL
END
DECLARE @inputDateWeekDay TINYINT = getNormalizedWeekDay(@inputDate, @dateFirst);
DECLARE @inputWeekStartDate DATE = DATEADD(DAY, -(@inputDateWeekDay-1), @inputDate);
DECLARE @firstDayOfYear DATE = DATEFROMPARTS(getFiscalYear(@inputDate, @firstFiscalMonth), @firstFiscalMonth, 1);
DECLARE @firstWeekStartDate DATE = @firstDayOfYear;
DECLARE @weekDayOfFirstDayOfYear TINYINT = getNormalizedWeekDay(@firstDayOfYear, @dateFirst);
-- The day is between the first day of year and the beginning of the second week -> 1st (partial) week for non-iso style
IF (@inputDate >= @firstDayOfYear AND @inputDate < DATEADD(DAY, -(@weekDayOfFirstDayOfYear-1), DATEADD(DAY, 7, @firstWeekStartDate))) BEGIN
RETURN 1;
END
-- Adjust the first day of the weeks to match with @dateFirst.
SET @firstWeekStartDate = DATEADD(DAY, -(@weekDayOfFirstDayOfYear-1), @firstWeekStartDate);
RETURN (DATEDIFF(DAY, @firstWeekStartDate, @inputWeekStartDate) / 7) + 1;
END
请记住,旧版SQL Server中可能不存在某些功能,我正在使用SQL Server 2016.
答案 2 :(得分:0)
您可以使用common table expression
(简称cte
)计数表创建此表。这包括手动指定前10行(select t from values...
位),然后在cross join
中多次cte
将其自身指数化,以指数方式创建更多行。因为我有6个交叉连接(t t1, t t2, t t3...
部分)我生成了一百万行(10^6
),然后在top
子句中计算出我实际需要的数量,然后使用{{ 1}}生成一个迭代列表,该列表按天数添加到期间开始日期。
这是一个可以应用函数的日期表,甚至可以在多个财政年度中使用。我建议您使用它来创建row_number
查找表而不是每次都运行:
Dates
输出:
declare @DateStart date = '20160701'
,@DateEnd date = '20170630';
with t(t) as (select t from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(t))
,d(d) as (select top(datediff(d,@DateStart,@DateEnd)+1) dateadd(d,row_number() over (order by (select null))-1,@DateStart) from t t1,t t21,t t31,t t4,t t5,t t6)
select ((dense_rank() over (order by dateadd(m,datediff(m,0,d),0)) - 1) % 12) + 1 as [Period]
,dense_rank() over (partition by dateadd(m,datediff(m,0,d),0)
order by case when dateadd(d,1-datepart(dw,d),d) < dateadd(m,datediff(m,0,d),0)
then dateadd(m,datediff(m,0,d),0)
else dateadd(d,1-datepart(dw,d),d)
end) as [Week]
,case when dateadd(d,1-datepart(dw,d),d) < dateadd(m,datediff(m,0,d),0)
then dateadd(m,datediff(m,0,d),0)
else dateadd(d,1-datepart(dw,d),d)
end as StartDate
,case when dateadd(d,7-datepart(dw,d),d) > dateadd(m,datediff(m,0,d)+1,0)
then dateadd(d,-1,dateadd(m,datediff(m,0,d)+1,0))
else dateadd(d,7-datepart(dw,d),d)
end as EndDate
,d as DayOfWeek
,year(d) as [Year]
from d
order by d;