繁体   English   中英

T-SQL计算一年中第一个ISO周的第一个星期一,并枚举所有周为从头到尾

[英]T-SQL calculate the first monday of the first ISO week in a year and enumerate all weeks as from-to periods

我想计算给定年份中第一个ISO 8601周的第一天(星期一),然后枚举给定年份中所有ISO 8601周(包括其编号)。 我想知道是否可以比到目前为止做得更好,也许使用内置函数datepart(iso_week,getdate())? 这是我的代码:

DECLARE @y as int = 2011

DECLARE @firstDayOfYear date = CAST(CAST(@y AS varchar(4)) + '-01-01' AS DATE)
--thursday before 1st Jan
DECLARE @Thursday date = DATEADD(day,
               3 - (DATEPART(dw, @firstDayOfYear) + @@DATEFIRST - 2) % 7,
               @firstDayOfYear) 
DECLARE @FirstDayOfIsoWeek date = DATEADD(day,
               - (DATEPART(dw, @firstDayOfYear) + @@DATEFIRST - 2) % 7,
               @firstDayOfYear)
if (@Thursday<@firstDayOfYear)
    SELECT @FirstDayOfIsoWeek  = DATEADD(d,7, @FirstDayOfIsoWeek) 

SELECT @FirstDayOfIsoWeek

我也正在寻找一种方法,以枚举从第一个星期一开始的所有ISO周为周期表,其中包含StartDate,EndDate,Year,Month,ISOWeekNo列。 如果有人知道快速干净的解决方案,请提供帮助。

接受的解决方案:

我进行了很少的编辑,因此可以根据需要进行工作-以Outlook日历方式枚举一年中的几周:

CREATE FUNCTION [dbo].[FGetISOWeeks](@y int)
RETURNS 
@ISOWeeks TABLE (StartDate Date NOT NULL, EndDate Date NOT NULL, YearNo int not null, MonthNo int not null, WeekNo int not null)
AS
BEGIN
    DECLARE @weeknumbers as TABLE ( weeknum int not null primary key (weeknum)) -- helper table of week numbers
    declare @weeknum int = 1
    while (@weeknum <= 53)
    begin
      insert @weeknumbers values(@weeknum)
      set @weeknum = @weeknum + 1
    end
    DECLARE @firstDayOfYear date = CAST(CAST(@y AS varchar(4)) + '-01-01' AS DATE)  
    DECLARE @Thursday date = DATEADD(day,3 - (DATEPART(dw, @firstDayOfYear) + @@DATEFIRST - 2) % 7,@firstDayOfYear)  --thursday before 1st Jan
    DECLARE @FirstDayOfIsoWeek date = DATEADD(day, - (DATEPART(dw, @firstDayOfYear) + @@DATEFIRST - 2) % 7, @firstDayOfYear)
    if (@Thursday<@firstDayOfYear) SELECT @FirstDayOfIsoWeek  = DATEADD(d,7, @FirstDayOfIsoWeek) -- calculate first day of iso year
    declare @Monday0 date = DATEADD(d,-7, @FirstDayOfIsoWeek)
    INSERT INTO @ISOWeeks
    select DATEADD(WEEK, N.weeknum, @Monday0) as StartDate
          ,DATEADD(day, 7*N.weeknum+6, @Monday0) as EndDate, @y as YearNo
          ,DATEPART(month,  DATEADD(DAY, 7*N.weeknum+3, @Monday0)) as MonthNo
          ,DATEPART(ISO_WEEK,  DATEADD(WEEK, N.weeknum, @Monday0)) as WeekNo    
    from @weeknumbers N
    where DATEPART(year,  DATEADD(day, 7*N.weeknum+3, @Monday0)) = @y
    order by N.weeknum  
    RETURN 
END

除非我误解了ISO周的含义,否则我认为以下代码对您有效。

第一部分看起来确实很丑陋,但是执行起来很快。 目的是查找一年中第一周的第一天。 如果不是星期一,那么第一天将在上一年,而我们要转到下周。

从那里开始,递归CTE简化了问题的第二部分。

DECLARE 
    @year CHAR(4) = 1972,
    @YearDate DATE

SELECT
     DATEADD(WEEK,DATEDIFF(WEEK,0,DATEADD(YEAR,DATEDIFF(YEAR,0,@year + '-01-01'),0)),0)
    ,DATEADD(WEEK,1 + DATEDIFF(WEEK,0,DATEADD(YEAR,DATEDIFF(YEAR,0,@year + '-01-01'),0)),0)

IF(DATENAME(YEAR,DATEADD(WEEK,DATEDIFF(WEEK,0,DATEADD(YEAR,DATEDIFF(YEAR,0,@year + '-01-01'),0)),0)) = @year)
BEGIN
SELECT @YearDate = DATEADD(WEEK,DATEDIFF(WEEK,0,DATEADD(YEAR,DATEDIFF(YEAR,0,@year + '-01-01'),0)),0)
END
ELSE
BEGIN
SELECT @YearDate = DATEADD(WEEK,1 + DATEDIFF(WEEK,0,DATEADD(YEAR,DATEDIFF(YEAR,0,@year + '-01-01'),0)),0)
END

;WITH DateCTE (StartDate, EndDate, YearNum, MnthName, WeekNumber) AS (
    SELECT
         @YearDate
        ,DATEADD(DAY,7,@YearDate)
        ,DATENAME(YEAR,@YearDate)
        ,DATENAME(MONTH,@YearDate)
        ,DATEDIFF(WEEK,DATEADD(WEEK,DATEDIFF(WEEK,0,@YearDate)-1,0),@YearDate)

    UNION ALL

    SELECT
         EndDate
        ,DATEADD(DAY,7,EndDate)
        ,DATENAME(YEAR,EndDate)
        ,DATENAME(MONTH,EndDate)
        ,DATEDIFF(WEEK,DATEADD(WEEK,DATEDIFF(WEEK,0,@YearDate)-1,0),EndDate)
    FROM DateCTE
    WHERE DATENAME(YEAR,EndDate) = @year
)
SELECT
*
FROM DateCTE

我将创建一个值从1到53的表

create table weeknumbers
(
  weeknum int not null
  primary key (weeknum)
)

declare @weeknum int
set @weeknum = 1

while (@weeknum <= 53)
begin
  insert weeknumbers values(@weeknum)
  set @weeknum = @weeknum + 1
end

然后,此存储的proc应该会给您正确的结果

create isoweeks(@y int) as
begin

DECLARE @firstDayOfYear date = CAST(CAST(@y AS varchar(4)) + '-01-01' AS DATE)
--thursday before 1st Jan
DECLARE @Thursday date = DATEADD(day,
               3 - (DATEPART(dw, @firstDayOfYear) + @@DATEFIRST - 2) % 7,
               @firstDayOfYear) 
DECLARE @FirstDayOfIsoWeek date = DATEADD(day,
               - (DATEPART(dw, @firstDayOfYear) + @@DATEFIRST - 2) % 7,
               @firstDayOfYear)
if (@Thursday<@firstDayOfYear)
    SELECT @FirstDayOfIsoWeek  = DATEADD(d,7, @FirstDayOfIsoWeek)

declare @Monday0 date = DATEADD(d,-7, @FirstDayOfIsoWeek)

-- SELECT @FirstDayOfIsoWeek

select DATEADD(WEEK, N.weeknum, @Monday0) as StartDate
, DATEADD(day, 7*N.weeknum+6, @Monday0) as EndDate
, DATEADD(day, 7*N.weeknum+6, @Monday0) as EndDate
, @y as Year
,   DATEPART(month,  DATEADD(WEEK, N.weeknum, @Monday0)) as Month
,   DATEPART(ISO_WEEK,  DATEADD(WEEK, N.weeknum, @Monday0)) as Month
from dbo.weeknumbers N
where DATEPART(year,  DATEADD(WEEK, N.weeknum, @Monday0)) = @y
order by N.weeknum

end

不知道这种解决方案是否比其他发布的解决方案更好。 也许更快或更慢,更容易理解与否。 我想这取决于您的偏好。 我使用您现有的代码作为存储过程的开头,但是您应该调整代码以简化星期一0(上一ISO年的最后一个星期一)的计算。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM