【问题标题】:Determine the date range of a calendar week in SQL在 SQL 中确定日历周的日期范围
【发布时间】:2022-01-24 12:41:35
【问题描述】:

例如,日期是“2021-12-13”

所以我检测到该日期的周数: datepart(wk, '2021-12-13') --> 51

我需要找到去年 51 周的星期一和星期日的日期

【问题讨论】:

  • 从您使用的功能我假设您使用的是 Microsoft SQL 方言,对吗?
  • 是的,它是 Microsoft SQL 方言
  • 使用 datename 函数传递适当的日期部分作为 select datename(dw, getdate())
  • 我会确保星期一是您一周的第一天,SET DATEFIRST 1,然后 WEHRE DATEPART(DW, columnName) = 1 或 DATEPART(dw, columnName) = 7

标签: sql date


【解决方案1】:

你可以从下面的sn-p开始

with
original as (select '2021-12-23' as d),
lastyear as (
  -- determine the same date last year
  select dateadd(yy, -1, d) as d
  from original
),
adjusted as (
  -- since we do not want the same date, but the same calendar week,
  -- we might need to add the difference between the two calendar weeks
  select dateadd(
    wk,
    datepart(wk, original.d)-datepart(wk, lastyear.d),
    lastyear.d) as d
  from lastyear, original
)
select
 -- finally determine the start and end of the week
 dateadd(dd, 2 - datepart(dw, adjusted.d), adjusted.d) as "start",
 dateadd(dd, 8 - datepart(dw, adjusted.d), adjusted.d) as "end",
from adjusted

不过,有几点需要注意:

  1. 当您的 SQL 服务器从星期日开始一周时,需要 2 和 8 以及最后的 dateadd(即 datepart(dw, ...) 返回 2 表示星期一)。最好在查询中读出服务器配置并使用适当的偏移量而不是硬编码
  2. adjusted CTE 是一种非常明确的方式来处理去年的同一日期可能会进入另一个日历周这一事实。肯定有更便宜的方法来实现这一点,但它们看起来也很复杂,而且它们在做什么也不那么“明显”。
  3. 我只玩了一点 SQL fiddle,所以这确实需要针对我可能遗漏的极端情况进行测试。
  4. 一个极端情况是日历周 53 有时表示一年中的最后一周,有时表示第一周,有时可能同时表示两者。我目前无法解决这个问题,因为我不知道你打算如何处理这个问题。
  5. ISO 周的正确日期部分是 isowk 而不是 wk。您可能想使用它,但是您必须考虑如何使用它。因为Microsoft SQL 不支持isowk 换成dateadd

【讨论】:

  • 非常感谢您的帮助。我还考虑了包含过去一年的日子和新年的日子的星期。像去年的第53周这样的一周是值得想象的。明年的第一周从第一个星期一开始。如果我们将此条件添加到此请求中,那就太好了。即使在示例“2021-12-23”中使用此日期,SQL 也会建议第 52 周,但如果第一周从第一个星期一开始而不是 1 月 1 日,则现在是第 51 周。
【解决方案2】:

星期和工作日数字取决于 DATEFIRST 设置。
但是您可以使用@@datefirst 变量来更正计算,使其不受DATEFIRST 设置的影响。

星期一

dateadd(day, 1-(@@datefirst-1)-datepart(weekday, '2021-12-13'), '2021-12-13')

星期日

dateadd(day, 7-(@@datefirst-1)-datepart(weekday, '2021-12-13'), '2021-12-13')

测试

set datefirst 1;
select *
, datepart(week, date_column) as week
, datepart(weekday, date_column) as weekday
, @@datefirst as df
, datename(weekday, date_column) as weekday_name
, [monday] = dateadd(day, 1-(@@datefirst-1)-datepart(weekday, date_column), date_column)
, [sunday] = dateadd(day, 7-(@@datefirst-1)-datepart(weekday, date_column), date_column)
from (values 
  (cast('2021-12-13' as date)),
  ('2021-12-14'), 
  ('2021-12-19')
) as val(date_column);
GO
date_column week weekday df weekday_name monday sunday
2021-12-13 51 1 1 Monday 2021-12-13 2021-12-19
2021-12-14 51 2 1 Tuesday 2021-12-13 2021-12-19
2021-12-19 51 7 1 Sunday 2021-12-13 2021-12-19

db小提琴here

或者您可以创建一个日历表并对其进行查询。

select cal_year, cal_df1_week
, min(cal_date) as begin_of_week
, max(cal_date) as end_of_week
from ref_calendar
where cal_year     = datepart(year, '2021-12-14')
  and cal_df1_week = datepart(week, '2021-12-14')
group by cal_year, cal_df1_week

日历表的创建可以参考here

【讨论】:

    【解决方案3】:

    请注意,因为给定年份第 51 周的星期日和星期一不一定是背靠背日期:

    set datefirst 1;
    select datepart(week, dt) as start_week, dt,
        datename(weekday, candidate) as candidate_day,
        datepart(week, candidate) as candidate_week,
        candidate
    from (values
        (cast('2021-12-13' as date)), ('2020-12-13'), ('2019-12-13'),
             ('2018-12-13'), ('2017-12-13'), ('2016-12-13'),
             ('2015-12-13'), ('2014-12-13'), ('2013-12-13')
    ) as t(dt)
    cross apply (values (-54), (-53), (-52), (-51), (-50)) as w(week_offset)
    cross apply (values
       (dateadd(week, week_offset, dateadd(day, -datepart(weekday, dt), dt)))) as v1(sunday)
    cross apply (values (sunday), (dateadd(day, 1, sunday))) as v2(candidate)
    where datepart(week, candidate) = 51
    order by dt desc, candidate;
    

    这个逻辑是在一个星期的范围内向后搜索,然后寻找与第 51 周匹配的日期。你会看到事情发生了很大的变化。我不确定这是你所期望的。

    【讨论】:

      猜你喜欢
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-02
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      • 2017-12-20
      相关资源
      最近更新 更多