【问题标题】:Problem with grouping on european standard week and year欧洲标准周和年分组问题
【发布时间】:2011-07-20 19:38:52
【问题描述】:

有一个日期部分 iso_week 返回选定日期的 iso_week。

SELECT DATEPART(iso_week, getdate())

这将返回周电流的欧洲标准。它不一定和星期一样。

棘手的部分来了:

SELECT DATEPART(iso_week, '2011-01-01')

返回

52 

所以它属于去年 为了按年和 iso_week 分组,我需要考虑 iso_week 不是来自同一年。

Iso_week 从星期一开始,属于大多数日子重叠的年份。所以 2010 年有 4 天,2011 年有 3 天,一周是 52 天,所有天都属于 iso_year 2010。但是 TSQL 没有 detepart iso_year。

declare @t table(col1 datetime)

insert @t 
ALL SELECT '2010-12-28'
UNION ALL SELECT '2010-12-29'
UNION ALL SELECT '2010-12-30'
UNION ALL SELECT '2010-12-31'
UNION ALL SELECT '2011-01-01'
UNION ALL SELECT '2011-01-02'
--UNION ALL SELECT '2011-01-03' Stackexchange is bugged and can't handle this line showing 53
UNION ALL SELECT '2011-01-04'

我需要类似的东西(iso_year 不存在):

SELECT DATEPART(ISO_WEEK, col1) WEEK, DATEPART(iso_YEAR, col1) YEAR, COUNT(*) COUNT 
FROM @t
GROUP BY DATEPART(ISO_WEEK, col1), DATEPART(iso_YEAR, col1)
ORDER BY  2,1

预期结果

WEEK  YEAR          COUNT
52    2010           6
1     2011           2

【问题讨论】:

    标签: tsql sql-server-2008 iso


    【解决方案1】:

    同一 ISO 周的星期四将明确地为您提供正确的年份。并且this answer 可以为您提供如何从给定日期获得正确的星期四的想法。

    SELECT
      Week  = DATEPART(ISOWK, TheThursday),
      Year  = DATEPART(YEAR, TheThursday),
      Count = COUNT(*)
    FROM (
      SELECT
        TheThursday = DATEADD(
          DAY,
          3 - (DATEPART(DW, col1) + @@DATEFIRST - 2) % 7,
          col1
        )
      FROM @t
    ) s
    GROUP BY
      TheThursday
    

    【讨论】:

    • 非常好,在不设置 firstdate 的情况下解决了问题,并处理了较高和较低的周休息时间。还描述了与 TheThursday 的计算。也请看一下我的版本。
    【解决方案2】:

    您可以检查 dayofyeariso_week 以确定是否需要用 1 减去年份。

    select datepart(iso_week, col1) as [week], 
           case when datepart(dayofyear, col1) < 7 and
                     datepart(iso_week, col1) > 51
                then year(col1) - 1
                else year(col1)
           end as [year], 
           count(*) as [count]
    from @t
    group by datepart(iso_week, col1),
             case when datepart(dayofyear, col1) < 7 and
                       datepart(iso_week, col1) > 51
                  then year(col1) - 1
                  else year(col1)
             end
    order by [year], [week]
    

    【讨论】:

    • 这是一个不错的解决方案,但如果还要处理像“2008-12-31”这样的日期(实际上是第 1 周),这将是一个很长的声明。Andriy 提出了一个更好的解决方案.
    【解决方案3】:

    已编辑...

    SELECT 
      IsoWeek  = DATEPART(ISO_WEEK, TheDate), 
      IsoYear  = CASE 
        WHEN 
          MONTH(TheDate) = 1 AND DATEPART(ISO_WEEK, TheDate) > 51 
          THEN YEAR(TheDate) - 1 
          ELSE YEAR(TheDate) 
        END, 
      DayCount = COUNT(*)
    FROM 
      @t
    GROUP BY 
      DATEPART(ISO_WEEK, TheDate), 
      CASE 
        WHEN MONTH(TheDate) = 1 AND DATEPART(ISO_WEEK, TheDate) > 51 
        THEN YEAR(TheDate) - 1 
        ELSE YEAR(TheDate) 
      END 
    ORDER BY  
      IsoYear,
      IsoWeek
    
    --IsoWeek  IsoYear  DayCount
    --     52     2010         6
    --      1     2011         2
    

    【讨论】:

    • 不好。一些 ISO 年有 53 周。见this。否则这看起来不错。
    • 已更正。感谢您发现缺陷 Chris。
    【解决方案4】:

    此解决方案还可以处理一周从前一年开始的年份。

    SELECT DATEPART(ISO_WEEK, col1) WEEK, 
    DATEPART(year , col1 - CAST(col1-.5 as int)%7 + 3) YEAR, 
    COUNT(*) COUNT 
    FROM @t
    GROUP BY DATEPART(ISO_WEEK, col1), 
    DATEPART(year , col1 - CAST(col1-.5 as int)%7 + 3)
    ORDER BY 2,1
    

    【讨论】:

    • 使用weekday 会让你依赖set datefirst 设置。你或许可以使用@@datefirst 来解决这个问题。
    • @Mikael Eriksson 我更改了答案,因此它不使用 datefirst。找到了更好的方法。我想你会觉得它很有趣
    • 最终你似乎也在使用星期四,只是你采取了不同的途径来解决这个问题。我认为这是利用 SQL Server 中日期的内部表示的巧妙方法。我会犹豫将它放入我公司的生产代码中,但我通常不介意在我自己的一些小项目中从这种简洁、智能的解决方案中受益。一件事:如果col1 有机会成为date 而不是datetime,则需要添加一些更改才能使其正常工作(dateint 不兼容)。
    • *Andriy 我打算把它放在一个函数中。这就是为什么我拼命地试图避免'set datefirst 1'(这不能在函数中完成)。我认为将日期传递给 datetime 函数可以正常工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 2020-09-24
    • 2018-01-13
    相关资源
    最近更新 更多