Sql server 理解Microsoft SQL中的ISO#U每周都有困难,什么';到底发生了什么事?

Sql server 理解Microsoft SQL中的ISO#U每周都有困难,什么';到底发生了什么事?,sql-server,Sql Server,我有一组8个月左右的数据,部分是2012年的数据,部分是2013年的数据。我不确定这是否是一种文化现象,但我是荷兰人,我相信数据库中的数据应该按照标准进行解释 我写了两个查询,一个是正则week表达式,另一个是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

我有一组8个月左右的数据,部分是2012年的数据,部分是2013年的数据。我不确定这是否是一种文化现象,但我是荷兰人,我相信数据库中的数据应该按照标准进行解释

我写了两个查询,一个是正则week表达式,另一个是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)
这两个查询的结果分别为:

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

我的问题如下:

  • 在第二个查询中(使用常规ww),本周53保存了哪些数据?据我所知,这是进入2013年的几天,但仍然属于第52周,但由于今年即将结束,这几天又增加了一周。这是正确的吗?如果没有,请告诉我
  • 在第一个查询(iso_周)中,第1周是什么?我相信这是第53周,如第二个查询(常规ww)所示。但它应该是2013年的第一周,或者可能会添加到2013年的第一周?我很难理解这里发生了什么
  • 即使对此进行了解释,我也不太明白为什么要返回14行数据。对于我的查询,我想从我提供的日期(示例中是硬编码的)起12周内返回。我怎么可能得到每周12行的合法数据,而不是13行或14行?如果我使用day而不是week,它可以追溯到12天,因此我有12行返回给我
感谢您的帮助,我希望我说的有道理。

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

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

SET DATEFIRST 3;
SELECT  [20121231] = DATEPART(WEEK, '20121231'),
        [20130101] = DATEPART(WEEK, '20130101'),
        [20130102] = DATEPART(WEEK, '20130102');
当您试图分析12周的数据时,有两个因素导致您得到14行数据,原因适用于ISO_WEEK和WEEK,您的日期范围是20121206到20130228,即使这些日期相隔12周,如果您的DATEFIRST与开始日期和结束日期的工作日不匹配,则您的日期范围将跨越13周

ISO_WEEK函数中的第14行是因为没有ISO_YEAR函数,在ISO标准中,2013年第1周开始于2012年12月31日,但是由于
DATEPART(YEAR
给出了2012年,因此将第1周分为两行:

Year    Week    DaysInRow
2012    1       1
2013    1       6
使用周的第14周是由于简单的计算方法,同一周(2012年12月31日至2013年1月6日)也分为2周,但如下所示

Year    Week    DaysInRow
2012    53      1
2013    1       6
如果你有一个日历表,那么你应该在上面有ISO_YEAR和ISO_WEEK列(如果你不添加它们),你很容易看到20121231是2013年的第一周,用于报告目的,如果你没有日历表(我建议你创建一个),你可以创建你自己的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
创建函数dbo.ISO_YEAR@Date-DATETIME
返回整数
作为
开始
声明@ISOyear INT=DATEPART(年,@Date);
--特殊情况:1月1日至3日可能属于上一年
如果(DATEPART(月,@DATE)=1,DATEPART(ISO_周,@DATE)>50)
设置@ISOyear=@ISOyear-1;
--特殊情况:12月29-31日可能属于下一年
如果(日期部分(月,日)=12和日期部分(ISO_周,日)<45)
设置@ISOyear=@ISOyear+1;
返回@ISOYear;
结束

我使用下面更简单的代码为我提供可靠的ISO年:

DATEPART(YY, @TheDate - (DATEPART(DW, @TheDate) - 4))
…我在下面编写了UDF(其中包括上面的代码片段),为我提供了一个可靠的ISO年周字符串,可用于按周数进行报告或排序

UDF强制执行一个两个字符的星期字符串,以避免在2016-10之后进行2016-9排序(我敢打赌你已经去过了,我当然去过了!):


非常感谢您如此详细的解释,这是非常有帮助的。我已经为此伤了我的头一段时间了,在寻找解释时,我发现了很多相互矛盾的信息。感谢您花时间用我提供的示例解释得如此清楚。有了这些知识,我想我可以实现我想要实现的目标我的问题。
DATEPART(YY, @TheDate - (DATEPART(DW, @TheDate) - 4))
CREATE FUNCTION [dbo].[ISOYearAndWeek] 
(@TheDate DATETIME) RETURNS NCHAR(7) AS
BEGIN

  DECLARE @Result NCHAR(7)

  SELECT @Result = 
    CONVERT(CHAR(4), DATEPART(YY, @TheDate - (DATEPART(DW, @TheDate) - 4)))
    + '-'
    + CONVERT(CHAR(2), RIGHT('00' + CONVERT(VARCHAR(2), DATEPART(ISOWK, @TheDate)), 2)) 

    RETURN @Result

END