【问题标题】:Number of weeks and partial weeks between two days calculated wrong两天之间的周数和部分周数计算错误
【发布时间】:2017-03-13 17:44:23
【问题描述】:

为什么返回 4 而不是 6?

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

week as datepart 是否仅计算 monday - sunday 的周数(整周)?如何获得所有周 - 包括那些没有 seven days 的?在上述情况下,答案应该是6

【问题讨论】:

  • 怎么应该是6?你的意思是 5,当然!
  • 为什么星期一是一周的第一天?
  • @Pream - 许多国家的一周从星期一开始。
  • 'datediff' 不使用 'datefirst' 设置 :( 看这里:stackoverflow.com/questions/1101892/…
  • @GiorgosAltanis - 2017 年 1 月有 6 周。星期日是一月第一周的唯一一天。

标签: sql-server tsql


【解决方案1】:

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 表格以显示您所期望的结果。

【讨论】:

  • 这正是我想要的。谢谢。
  • 我能问一下您是如何在“结果”中获得结果的 - 标题列下方的行并与列对齐?您是否以某种方式从 SSMS 复制了此内容?
  • @FrenkyB - “Results to Text”而不是“Results to Grid” - 通过“Query -> Results To”菜单或工具栏按钮之一提供(在我当前的设置中,~10 个图标在“执行”按钮的右侧)(虽然我也做了一个偷偷摸摸的作弊并阻止编辑它以删除相当大的浪费空间,因为我在定义 @987654327 时做了我通常的“如果没有提供则选择荒谬的数据类型” @表)
  • 非常感谢,我现在看到了。
【解决方案2】:

不过,您要问的是一个范围涵盖了多少周,而不是两个日期之间有多少周。

DATEDIFF 在计算周转换时总是使用星期日。 这不是错误,这样做是为了确保函数是确定性的,并为每个查询返回相同的值,无论DATEFIRST 设置如何。来自documentation

指定 SET DATEFIRST 对 DATEDIFF 没有影响。 DATEDIFF 始终使用星期日作为一周的第一天,以确保函数具有确定性。

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

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

这是一个脆弱的计算,但如果 DATEFIRST 更改或其中一个日期在不同的年份,则会中断。

您可以使用 ISO 周来摆脱 SET DATEFIRST

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

但这对于2017-01-01 会失败,因为星期日被算作上一年的第 52 周。

不过,更好的解决方案是使用包含日期和不同周数的 Calendar 表来计算不同的周数,以满足多种业务需求,例如正常和 ISO 周数,或基于 @ 的业务日历987654323@日历。

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

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'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    • 2020-08-21
    • 1970-01-01
    • 2019-04-15
    相关资源
    最近更新 更多