两天之间的周数和部分周计算错误

时间:2017-03-13 07:06:02

标签: sql-server tsql

为什么这会返回4而不是6?

SET DATEFIRST 1
SELECT DATEDIFF(WEEK, CAST('2017-01-01' AS DATE), CAST('2017-01-31' AS DATE))

作为日期部分的星期是否只计算monday - sunday(整周)的几周?如何获得所有周 - 包括那些没有seven days的人?在上述情况下,答案应为6

2 个答案:

答案 0 :(得分:4)

DATEDIFF计算过渡,而非句点(例如,查看DATEDIFF(year,'20161231','20170101'))。它还将星期日视为一周的第一天。那么,我们如何补偿这些功能呢?首先,我们改变日期,以便星期一是新的星期日,第二,我们加1以补偿Fence-Post错误:

declare @Samples table (
    StartAt date not null,
    EndAt date not null,
    SampleName varchar(93) not null
)
insert into @Samples (StartAt,EndAt,SampleName) values
('20170101','20170131','Question - 6'),
('20170102','20170129','Exactly 4'),
('20170102','20170125','3 and a bit, round to 4'),
('20170101','20170129','4 and 1 day, round to 5')
--DATEDIFF counts *transitions*, and always considers Sunday the first day of the week
--We subtract a day from each date so that we're effectively treating Monday as the first day of the week
--We also add one because DATEDIFF counts transitions but we want periods (FencePost/FencePanel)
select *,
    DATEDIFF(WEEK, DATEADD(day,-1,StartAt), DATEADD(day,-1,EndAt)) +1
    as NumWeeks
from @Samples

结果:

StartAt    EndAt      SampleName                 NumWeeks
---------- ---------- -------------------------- -----------
2017-01-01 2017-01-31 Question - 6               6
2017-01-02 2017-01-29 Exactly 4                  4
2017-01-02 2017-01-25 3 and a bit, round to 4    4
2017-01-01 2017-01-29 4 and 1 day, round to 5    5

如果这与您想要的不匹配,也许您可​​以采用并调整我的@Samples表来显示您期望的结果。

答案 1 :(得分:2)

你问的是,一个范围涵盖的周数,而不是两个日期之间的周数。

在计算周转换时,

DATEDIFF总是使用星期日。 这不是一个错误,它可以确保函数是确定性的,并且对于每个查询都返回相同的值,无论DATEFIRST设置如何。来自documentation

  

指定SET DATEFIRST对DATEDIFF没有影响。 DATEDIFF总是使用星期日作为一周的第一天,以确保功能是确定性的。

一种解决方案是计算开始日期和结束日期的周数之间的差异,第一天是星期一。 1被添加到差异中以考虑第一周:

SET DATEFIRST 1;
select 1 +datepart(WEEK,'20170131') - datepart(WEEK,'20170101') 

这是一个脆弱的计算,但如果DATEFIRST发生变化或其中一个日期是在另一年,则会中断。

您可以使用ISO Weeks摆脱SET DATEFIRST

select 1 +datepart(ISO_WEEK,'20170131') - datepart(ISO_WEEK,'20170101') 

2017-01-01会失败,因为星期日被计算为上一年的第52周。

更好的解决方案是使用包含日期和不同周数的Calendar表计算不同的周数,以涵盖多个业务要求,例如正常和ISO周数,或基于的业务日历一个4-4-5日历。

在这种情况下,您可以只计算不同的周数:

SELECT COUNT(DISTINCT Calendar.IsoWeek )
from Calendar
where date between '20170101' and '20170131'

如果表格没有ISO周列,您可以使用DATEPART

select count (distinct datepart(ISO_WEEK,date) )
from Calendar
where date between '20170101' and '20170131'