【问题标题】:How to get Monday, Sunday based on a Week Number in SQL Server?如何根据 SQL Server 中的周数获取星期一、星期日?
【发布时间】:2011-06-20 05:11:26
【问题描述】:

我有一个星期数(例如 23),我想获取该周的星期一和星期日的日期。
我假设星期一是一周的第一天(例如,我的脚本中有 SET DATEFIRST 1)。

【问题讨论】:

  • 今年是 2011 年 1 月 3 日的第二个星期一 (*) 或第一个星期一。对于前者,“2011 年第一周”将没有星期一
  • 请定义第 1 周。国际标准化组织?国家? en.wikipedia.org/wiki/ISO_week_date#Examples
  • 在 SQL Server 中使用 Datepart(Week) 以可逆的方式定义它是有意义的,我的答案将提供。

标签: sql sql-server-2005 date


【解决方案1】:

您可以尝试一些 dateadd 逻辑...

dateadd(week, 23, '2011-01-01')

dateadd(day, 7, dateadd(week, 23, '2011-01-01'))

更新:

select dateadd(day, 23, dateadd(week, 23, '2011-01-01')) , Datename(weekday,dateadd(day, 23, dateadd(week, 23, '2011-01-01')));
// RETURNS 7/4/2011,    Monday
select  Datename(weekday,dateadd(day, 23, dateadd(week, 23, '2011-01-01')));
// RETURNS  Monday

示例来自:http://msdn.microsoft.com/en-us/library/ms186819.aspx

DECLARE @datetime2 datetime2 = '2007-01-01 13:10:10.1111111'
SELECT 'year', DATEADD(year,1,@datetime2)
UNION ALL
SELECT 'quarter',DATEADD(quarter,1,@datetime2)
UNION ALL
SELECT 'month',DATEADD(month,1,@datetime2)
UNION ALL
SELECT 'dayofyear',DATEADD(dayofyear,1,@datetime2)
UNION ALL
SELECT 'day',DATEADD(day,1,@datetime2)
UNION ALL
SELECT 'week',DATEADD(week,1,@datetime2)
UNION ALL
SELECT 'weekday',DATEADD(weekday,1,@datetime2)
UNION ALL
SELECT 'hour',DATEADD(hour,1,@datetime2)
UNION ALL
SELECT 'minute',DATEADD(minute,1,@datetime2)
UNION ALL
SELECT 'second',DATEADD(second,1,@datetime2)
UNION ALL
SELECT 'millisecond',DATEADD(millisecond,1,@datetime2)
UNION ALL
SELECT 'microsecond',DATEADD(microsecond,1,@datetime2)
UNION ALL
SELECT 'nanosecond',DATEADD(nanosecond,1,@datetime2);
/*
Year         2008-01-01 13:10:10.1111111
quarter      2007-04-01 13:10:10.1111111
month        2007-02-01 13:10:10.1111111
dayofyear    2007-01-02 13:10:10.1111111
day          2007-01-02 13:10:10.1111111
week         2007-01-08 13:10:10.1111111
weekday      2007-01-02 13:10:10.1111111
hour         2007-01-01 14:10:10.1111111
minute       2007-01-01 13:11:10.1111111
second       2007-01-01 13:10:11.1111111
millisecond  2007-01-01 13:10:10.1121111
microsecond  2007-01-01 13:10:10.1111121
nanosecond   2007-01-01 13:10:10.1111111
*/

【讨论】:

  • 不幸的是,dateadd(week, ... 似乎只是增加了 7 天。所以在 2011 年使用这种方法,每周从星期六开始。
  • 应该加上第二个参数中指定的周数(上面的23)...查看更新的sql和上面的结果...
  • 现在您要增加 23 周和 23 天:dateadd(day, 23, dateadd(week, 23, '2011-01-01')) 不知道应该怎么做。
  • 对...我会更正它应该增加 7 天,谢谢
  • "DATEADD" 是一个内置的 MSSQL(和 TSQL)函数,它采用 3 个参数; “datepart”指定在日期的哪一部分返回新值,“number”用于增加 datepart,“date”表示添加过程的“开始日期”。在我上面来自 msdn 站点的示例代码中,他们使用 2007-01-01 作为开始日期...我在上面的前 2 个示例中使用了 2011-01-01。
【解决方案2】:
Declare @StartDate datetime;
Set @StartDate = '20110101';

With StartOfWeek As
    (
    Select DateAdd(
        week
        , 23
        , DateAdd(
            d 
            , -(DatePart(dw, @StartDate) - 1)
            , @StartDate
            ) ) As Sunday
    )
Select Sunday, DateAdd(d,1,Sunday) As Monday
From StartOfWeek

【讨论】:

    【解决方案3】:
    DECLARE @startweek1 datetime
    SET DATEFIRST 1
    
    --first monday of year
    SELECT @startweek1 = DATEADD(day, 8-DATEPART(weekday, '2011-01-01'), '2011-01-01')
    
    --day based
    SELECT
        DATEADD(day, 22 * 7, @startweek1) AS MondayWeek23,
        DATEADD(day, 23 * 7 -1 , @startweek1) AS SundayWeek23
    
    --week based
    SELECT
        DATEADD(week, 22, @startweek1) AS MondayWeek23,
        DATEADD(day, -1, DATEADD(week, 23 , @startweek1)) AS SundayWeek23
    

    编辑:

    如果第 1 周不像 Andomar 所说的那样从第 1 天开始,则此解决方案有效

    编辑 2:

    根据Wikipedia:2008-12-29 是 ISO 2009 年第 1 周的第 1 天。

    正如 Andomar 所说,week numbers vary

    Cyber​​kiwi 提到此代码在 2007 年是错误的:它的错误频率远不止于此。这同样适用于他的代码,它与 2007 年相匹配,但其他代码同样错误。

    【讨论】:

    • +1 打败我,这是正确的方法。有时新年的第一周必须有 3 天,因此根据您所在的国家/地区,这可能会变得更加复杂。
    • @Andomar:我不知道。有第 53 周已经够糟糕了,更不用说关于年初的一些特殊规定了
    • @cyberkiwi:常量可能会关闭,但这个想法很好。在我的系统上它应该是DATEADD(day, 2-DATEPART(weekday, '2007-01-01'), '2007-01-01')(即只需将 8 更改为 2)
    • @gbn Sorry for not being OCD enough to try 没有在公共论坛上提供经过良好测试的代码的糟糕借口,倾向于将其他人标记为OCD。我要求道歉
    • @cyberwiki;你的方法好像和gbn一样,X - datepart(dw, ...),这个方法不错。
    【解决方案4】:

    注意: 任何以假设年份为 2011 年开始的答案都可以采用静态的第一个日期,即在下面的最后一个查询中将 @firstMon 替换为静态日期 '20101227 '。

    SET DATEFIRST 1
    
    declare @targetYear int
    declare @targetWeek int
    select @targetYear = 2011, @targetWeek = 23
    
    declare @firstMon datetime
    set @firstMon = dateadd(d,1-datepart(dw, right(@targetYear,4)+'0101'),right(@targetYear,4)+'0101')
    
    select
        MonOfTargetWeek = dateadd(week, @targetWeek-1, @firstMon),
        SunOfTargetWeek = dateadd(week, @targetWeek-1, @firstMon+6)
    

    以及真正的最终查询 - 当它在该周不存在时返回 NULL 的 Mon/Sun,例如partial-week-one 的 Mon 或 partial-week-53 的 Sun

    ;with tmp(Mon, Sun) as (
    select
        MonOfTargetWeek = dateadd(week, @targetWeek-1, @firstMon),
        SunOfTargetWeek = dateadd(week, @targetWeek-1, @firstMon+6)
    )
    select
        RealMonday = case when Year(Mon)=@targetYear then Mon end,
        RealSunday = case when Year(Sun)=@targetYear then Sun end
    from tmp
    

    对于 GBN 声称这个答案是错误的(在他的答案内),我在正确性证明下方提交

    我已经把代码变成了一个函数。

    CREATE function dbo.getMonSunForWeek(@targetYear int, @targetWeek int)
    returns table as return
    with pre(firstMon) as (
        select dateadd(d,1-datepart(dw, right(@targetYear,4)+'0101'),right(@targetYear,4)+'0101'))
    , tmp(Mon, Sun) as (
    select
        dateadd(week, @targetWeek-1, firstMon),
        dateadd(week, @targetWeek-1, firstMon+6)
        from pre
    )
    select
        Mon, Sun,
        RealMonday = case when Year(Mon)=@targetYear then Mon end,
        RealSunday = case when Year(Sun)=@targetYear then Sun end
    from tmp
    GO
    

    在下面我展示了从 1950 年到 2047 年的整个年和周 (1-53) 范围。在 每一个情况下,当星期一/星期日已确定时,使用DATEPART(week) 向后工作,SQL Server 同意函数中的星期编号。

    set datefirst 1;
    select
        [Year]  = years.number,
        [Week]  = weeks.number,
        [Mon]   = fun.realmonday,
        [Sun]   = fun.realsunday,
        [WeekX] = isnull(datepart(week, fun.realmonday), datepart(week, fun.realsunday)),
        [MonX]  = fun.Mon,
        [SunX]  = fun.Sun
    from master..spt_values years
    inner join master..spt_values weeks on weeks.type='P' and weeks.number between 1 and 53
    cross apply dbo.getMonSunForWeek(years.number, weeks.number) fun
    where years.type='P' and years.number between 1950 and 2047
    order by [year], [Week]
    

    2005-2006 年转换前后的输出

                                                     when including prior/next year
    Year Week Mon-of-week Sun-of-week datepart(week) Mon  -and-  Sun 
    2005 52   2005-12-19  2005-12-25  52             2005-12-19  2005-12-25
    2005 53   2005-12-26  NULL        53             2005-12-26  2006-01-01
    2006  1   NULL        2006-01-01   1             2005-12-26  2006-01-01
    2006  2   2006-01-02  2006-01-08   2             2006-01-02  2006-01-08
    

    【讨论】:

    • 2006 年失败。它给出了 2005 年 12 月 26 日,即 2005 年的第 52 周。en.wikipedia.org/wiki/ISO_week_date#Examples 我有足够的强迫症来检查 为什么 我们都错了...
    • @gbn - 它不会失败,只是你没有正确阅读。请参阅添加到答案的部分。
    【解决方案5】:

    日历表使这种日期查询变得非常简单。日历表只是在加载时计算和存储相关的日历详细信息,而不是在运行时计算。根据应用程序,这可以大大加快速度。这种方式与 dbms 无关;它只是在 WHERE 子句中使用文字。

    select cal_date from calendar
    where iso_year = 2011 
      and iso_week = 23
      and cal_dow in ('Mon', 'Sun');
    

    另一种仅依赖于日期表的方式。

    select cal_date from calendar
    where extract(isoyear from cal_date) = 2011 
      and extract(week from cal_date) = 23 
      and (extract(isodow from cal_date) = 1 or
           extract(isodow from cal_date) = 7);
    

    EXTRACT() 是标准SQL,但我不确定isoyear、week、isodow 子字段的名称是不是标准SQL。

    【讨论】:

      【解决方案6】:

      我们可以使用 DATEFIRST 变量来设置一周的开始日期。默认情况下,一周从星期日 (7) 开始。为了从星期一开始,我们将其值设置为 1。

      # Printing default value
      Select @@DATEFIRST; -- This will give 7 value i.e. default value
      
      # Changing first day of the week to Monday.
      Declare @CurrentWeekNumber int
      SET DATEFIRST 1
      set @CurrentWeekNumber= (SELECT DATEPART(WEEK, GETDATE()))
      select @CurrentWeekNumber
      

      PS:对 DATEFIRST 的更改对当前会话有效,即它不会影响其他可能依赖于不同周开始日期的数据库查询。

      【讨论】:

      • 所提供的答案被标记为低质量帖子以供审核。这个提供的答案可能是正确的,但它可以从解释中受益。仅代码答案不被视为“好”答案。以下是How do I write a good answer? 的一些指南。来自review
      猜你喜欢
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-24
      • 2022-01-05
      • 1970-01-01
      相关资源
      最近更新 更多