在Microsoft SQL中理解ISO_week和周的困难,究竟发生了什么?

Khe*_*ran 2 sql-server

我有一组8个月左右的数据,部分是2012年和部分2013年的数据.我不确定这是不是文化的东西,但我是荷兰人,我相信数据库中的数据应该用这个标准来解释.

所以我写了两个查询,一个是常规周表达式,一个是ISO_week,这个查询看起来像这样:

SELECT datepart(ISO_WEEK,date_dtm) as date_detail,
datepart(yyyy,date_dtm) as date_year, 
(sum(sum_value)/sum(user_count)) as value 

FROM kpi_record 
WHERE kpi_series_id = '15'
AND date_dtm > dateadd(ww,-12,'2013-02-28 00:00:00.000')
group by datepart(ISO_WEEK,date_dtm), datepart(yyyy,date_dtm)
ORDER BY datepart(yyyy,date_dtm), datepart(ISO_WEEK,date_dtm)

--

SELECT datepart(ww,date_dtm) as ww,
datepart(yyyy,date_dtm) as date_year, 
(sum(sum_value)/sum(user_count)) as value 

FROM kpi_record 
WHERE kpi_series_id = '15'
AND date_dtm > dateadd(ww,-12,'2013-02-28 00:00:00.000')
group by datepart(yyyy,date_dtm), datepart(ww,date_dtm)
ORDER BY datepart(yyyy,date_dtm), datepart(ww,date_dtm)
Run Code Online (Sandbox Code Playgroud)

这两个查询的结果分别为:

1   2012    7,14
49  2012    7,31475409836066
50  2012    7,39261285909713
51  2012    7,47905477980666
52  2012    7,30618401206636
1   2013    7,49925705794948
2   2013    7,26598837209302
3   2013    7,24533333333333
4   2013    7,22245989304813
5   2013    6,96774193548387
6   2013    7,24523160762943
7   2013    7,14718019257221
8   2013    7,34691195795007
9   2013    7,23430962343096
Run Code Online (Sandbox Code Playgroud)

49  2012    7,4537037037037
50  2012    7,33109017496635
51  2012    7,4656652360515
52  2012    7,36874051593323
53  2012    7,13888888888889
1   2013    7,50515463917526
2   2013    7,33190271816881
3   2013    7,17693315858453
4   2013    7,24209378407852
5   2013    7,0201072386059
6   2013    7,19281914893617
7   2013    7,17278911564626
8   2013    7,3283378746594
9   2013    7,24733096085409
Run Code Online (Sandbox Code Playgroud)

我按此顺序提出的问题是:

  • 在第二个查询中(使用常规ww),本周53有什么值得拥有的数据?据我所知,这是进入2013年的几天,但仍然属于第52周,但由于这一年结束,它为这些日子增加了一周.它是否正确?如果没有,请赐教.
  • 在第一个查询(iso_week)中,第1周是什么?我相信这是第53周,如第二次查询(常规ww)所示.但它应该是2013年的第1周,或者可能是2013年的第一周?我无法理解这里发生了什么.
  • 即使解释了这一点,我也不太明白为什么要返回14行数据.根据我的询问,我想回到我提供的12天后的时间(在示例中硬编码).我怎样才能获得每周12行的合法数据,而不是13或14?如果我使用一天而不是一周,它会回溯12天,因此我有12行返回给我.

谢谢你的帮助,我希望我有意义.

Gar*_*thD 10

ISO标准定义了一年中的第一周(其中周是周一至周日),其中4周或更多天属于该周(即该周的大部分时间是该年),可以简化为一年的第一周包含星期四.

DATEPART(WEEK,更简单,只是DATEFIRST从年初开始经过的周边界限(定义)的计数,始终从1开始,因此您可以连续三天获得三个不同的周数,如下所示:

SET DATEFIRST 3;
SELECT  [20121231] = DATEPART(WEEK, '20121231'),
        [20130101] = DATEPART(WEEK, '20130101'),
        [20130102] = DATEPART(WEEK, '20130102');
Run Code Online (Sandbox Code Playgroud)

在尝试分析12周的数据时,有两个因素导致您获得14行,原因同时适用于ISO_WEEK和WEEK,您的日期范围是20121206到20130228,即使这些日期相隔12周,如果您的DATEFIRST与开始日期和结束日期的工作日不匹配,那么您的日期范围将跨越13周.

ISO_WEEK函数中的第14行是因为没有ISO_YEAR函数,在ISO标准2013年第1周开始于2012年12月31日,但是因为DATEPART(YEAR2012年它将第1周分成2行:

Year    Week    DaysInRow
2012    1       1
2013    1       6
Run Code Online (Sandbox Code Playgroud)

使用WEEK的第14周是因为简单的计算方法,同一周(2012年12月31日 - 2013年1月6日)也分为2,但如下

Year    Week    DaysInRow
2012    53      1
2013    1       6
Run Code Online (Sandbox Code Playgroud)

如果你有一个日历表,那么你应该有ISO_YEAR和ISO_WEEK列(如果你不添加它们)你很容易看到20121231是2013年第1周的报告目的,如果你没有日历表(I建议你创建一个)你可以创建自己的UDF:

CREATE FUNCTION dbo.ISO_YEAR @Date DATETIME
RETURNS INT
AS
BEGIN
    DECLARE @ISOyear INT = DATEPART(YEAR, @Date);

    -- Special cases: Jan 1-3 may belong to the previous year
    IF (DATEPART(MONTH, @DATE) = 1 AND DATEPART(ISO_WEEK, @DATE) > 50)
        SET @ISOyear = @ISOyear - 1;

    -- Special case: Dec 29-31 may belong to the next year
    IF (DATEPART(MONTH, @DATE) = 12 AND DATEPART(ISO_WEEK, @DATE) < 45)
        SET @ISOyear = @ISOyear + 1;

    RETURN @ISOYear;
END
Run Code Online (Sandbox Code Playgroud)