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

时间:2014-06-20 13:49:25

标签: sql sql-server date iso8601

我想在给定年份的第一个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

2 个答案:

答案 0 :(得分:1)

除非我误解了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 :(得分:1)

我会创建一个值为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

然后这个存储过程应该给你正确的结果

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

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