【问题标题】:Get first day of week in SQL Server在 SQL Server 中获取一周的第一天
【发布时间】:2011-11-02 09:21:05
【问题描述】:

我正在尝试按周对记录进行分组,将聚合日期存储为一周的第一天。但是,我用于四舍五入日期的标准技术似乎不适用于周(尽管它适用于天、月、年、季度和我已应用的任何其他时间范围)。

这里是 SQL:

select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);

这将返回 2011-08-22 00:00:00.000,它是星期一,而不是星期日。选择@@datefirst 返回7,这是周日的代码,所以据我所知服务器设置正确。

我可以通过将上面的代码更改为:

select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);

但我必须做出这样的例外的事实让我有点不安。另外,如果这是一个重复的问题,我们深表歉意。我发现了一些相关的问题,但没有一个专门针对这个方面。

【问题讨论】:

  • (@@DATEFIRST + DATEPART(DW, @SomeDate)) % 7 保持不变,无论我认为@@datefirst 设置如何。星期一 = 2。

标签: sql-server tsql sql-server-2008 date


【解决方案1】:

谷歌搜索了这个脚本:

create function dbo.F_START_OF_WEEK
(
    @DATE           datetime,
    -- Sun = 1, Mon = 2, Tue = 3, Wed = 4
    -- Thu = 5, Fri = 6, Sat = 7
    -- Default to Sunday
    @WEEK_START_DAY     int = 1 
)
/*
Find the fisrt date on or before @DATE that matches 
day of week of @WEEK_START_DAY.
*/
returns     datetime
as
begin
declare  @START_OF_WEEK_DATE    datetime
declare  @FIRST_BOW     datetime

-- Check for valid day of week
if @WEEK_START_DAY between 1 and 7
    begin
    -- Find first day on or after 1753/1/1 (-53690)
    -- matching day of week of @WEEK_START_DAY
    -- 1753/1/1 is earliest possible SQL Server date.
    select @FIRST_BOW = convert(datetime,-53690+((@WEEK_START_DAY+5)%7))
    -- Verify beginning of week not before 1753/1/1
    if @DATE >= @FIRST_BOW
        begin
        select @START_OF_WEEK_DATE = 
        dateadd(dd,(datediff(dd,@FIRST_BOW,@DATE)/7)*7,@FIRST_BOW)
        end
    end

return @START_OF_WEEK_DATE

end
go

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47307

【讨论】:

    【解决方案2】:

    也许你需要这个:

    SELECT DATEADD(DD, 1 - DATEPART(DW, GETDATE()), GETDATE())
    

    或者

    DECLARE @MYDATE DATETIME
    SET @MYDATE = '2011-08-23'
    SELECT DATEADD(DD, 1 - DATEPART(DW, @MYDATE), @MYDATE)
    

    功能

    CREATE FUNCTION [dbo].[GetFirstDayOfWeek]
    ( @pInputDate    DATETIME )
    RETURNS DATETIME
    BEGIN
    
    SET @pInputDate = CONVERT(VARCHAR(10), @pInputDate, 111)
    RETURN DATEADD(DD, 1 - DATEPART(DW, @pInputDate),
                   @pInputDate)
    
    END
    GO
    

    【讨论】:

    • DATEPART(DW 依赖于@@datefirst
    • 我喜欢这个简单的。对于非常大的数据集,它似乎也能很好地运行。
    • 为什么不直接将输入参数设置为DATE,然后您不必对VARCHAR 进行任何次优转换,然后返回只是为了去除传入的任何意外时间分量。跨度>
    • 使用了转换函数,因为返回的值不需要Time 值。
    • 是的,但关键是转换为 varchar 并再次返回是昂贵的。如果您只有一个 DATE 参数,那么您不在乎是否包含时间......它会为您剥离。
    【解决方案3】:

    这对我来说非常有用:

    创建函数 [dbo].[StartOfWeek] ( @INPUTDATE 日期时间 ) 返回日期时间 作为 开始 -- 这在函数中不起作用。 -- SET DATEFIRST 1 -- 将星期一设置为一周的第一天。 DECLARE @DOW INT -- 存储星期几 SET @INPUTDATE = CONVERT(VARCHAR(10), @INPUTDATE, 111) 设置@DOW = DATEPART(DW,@INPUTDATE) -- 星期一到 1、星期二到 2 等的魔术转换。 -- 不考虑 SQL 服务器对本周开始的看法。 -- 但是这里我们将星期日标记为 0,但我们稍后会解决这个问题。 设置@DOW = (@DOW + @@DATEFIRST - 1) %7 IF @DOW = 0 SET @DOW = 7 -- 修复周日 返回日期添加(DD,1 - @DOW,@INPUTDATE) 结尾

    【讨论】:

    • 根据今天的日期,这似乎返回星期一,而不是星期日。 OP 已经有一个返回星期一的函数,他希望它返回星期天。 :-)
    • 哇!下次我应该更仔细地阅读问题。但是,如果仍然需要,我的解决方案可以轻松调整。无论如何,OP似乎对接受的答案感到满意-)
    • 这是我机器上的正确解决方案,因为对我来说: DATEADD(ww, DATEDIFF(ww,0, CONVERT(DATE,'2017-10-8') ), 0) 返回 2017- 10-9!
    【解决方案4】:

    回答为什么是星期一而不是星期日:

    您要向日期 0 添加周数。日期 0 是什么? 1900-01-01。 1900 年 1 月 1 日是哪一天?周一。所以在你的代码中你说的是,自 1900 年 1 月 1 日星期一以来已经过去了多少周?我们称之为[n]。好的,现在将 [n] 周添加到 1900 年 1 月 1 日星期一。您应该不会对这最终成为星期一感到惊讶。 DATEADD 不知道您想增加周数,但直到您到达周日,它只是增加 7 天,然后再增加 7 天,......就像 DATEDIFF 只识别已跨越的边界。例如,它们都返回 1,尽管有些人抱怨应该内置一些合理的逻辑来向上或向下舍入:

    SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31');
    SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');
    

    回答如何获得星期天:

    如果您想要星期天,请选择一个不是星期一而是星期日的基准日期。例如:

    DECLARE @dt DATE = '1905-01-01';
    SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);
    

    如果您更改 DATEFIRST 设置(或者您的代码正在为具有不同设置的用户运行),这不会中断 - 前提是无论当前设置如何,您仍然想要星期天。如果您希望这两个答案能够解决问题,那么您应该使用 确实 依赖于 DATEFIRST 设置的函数,例如

    SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
    

    因此,如果您将DATEFIRST 设置更改为星期一、星期二,那么您的行为将会改变。根据您想要的行为,您可以使用以下功能之一:

    CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday
    (
        @d DATE
    )
    RETURNS DATE
    AS
    BEGIN
        RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @d), '19050101'));
    END
    GO
    

    ...或...

    CREATE FUNCTION dbo.StartOfWeek2 -- always the DATEFIRST weekday
    (
        @d DATE
    )
    RETURNS DATE
    AS
    BEGIN
        RETURN (SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, @d), @d));
    END
    GO
    

    现在,您有很多选择,但哪一个表现最好?如果会有任何重大差异,我会感到惊讶,但我收集了迄今为止提供的所有答案,并通过两组测试对它们进行了测试——一组便宜,一组昂贵。我测量了客户端统计数据,因为我没有看到 I/O 或内存在此处的性能中发挥作用(尽管这些可能会根据函数的使用方式发挥作用)。在我的测试中,结果是:

    “便宜”赋值查询:

    Function - client processing time / wait time on server replies / total exec time
    Gandarez     - 330/2029/2359 - 0:23.6
    me datefirst - 329/2123/2452 - 0:24.5
    me Sunday    - 357/2158/2515 - 0:25.2
    trailmax     - 364/2160/2524 - 0:25.2
    Curt         - 424/2202/2626 - 0:26.3
    

    “昂贵的”分配查询:

    Function - client processing time / wait time on server replies / total exec time
    Curt         - 1003/134158/135054 - 2:15
    Gandarez     -  957/142919/143876 - 2:24
    me Sunday    -  932/166817/165885 - 2:47
    me datefirst -  939/171698/172637 - 2:53
    trailmax     -  958/173174/174132 - 2:54
    

    如果需要,我可以转达我的测试细节 - 在这里停下来,因为这已经变得相当冗长了。考虑到计算和内联代码的数量,看到 Curt 在高端领域中速度最快,我有点惊讶。也许我会进行一些更彻底的测试并写博客……如果你们不反对我在其他地方发布你们的函数。

    【讨论】:

    • 所以,如果我认为我的周从周日开始并在周六结束,我可以得到一周中任何日期的 最后 天@d,如下所示:SELECT DATEADD (wk, DATEDIFF(wk, '19041231', @d), '19041231')
    【解决方案5】:

    我对这里给出的任何答案都没有任何问题,但是我确实认为我的实现和理解要简单得多。我没有对其进行任何性能测试,但应该可以忽略不计。

    因此,我从日期以整数形式存储在 SQL Server 中这一事实得出我的答案(我说的只是日期组件)。如果你不相信我,试试这个 SELECT CONVERT(INT, GETDATE()),反之亦然。

    现在知道了这一点,您就可以做一些很酷的数学方程式了。你也许能想出一个更好的,但这是我的。

    /*
    TAKEN FROM http://msdn.microsoft.com/en-us/library/ms181598.aspx
    First day of the week is
    1 -- Monday
    2 -- Tuesday
    3 -- Wednesday
    4 -- Thursday
    5 -- Friday
    6 -- Saturday
    7 (default, U.S. English) -- Sunday
    */
    
    --Offset is required to compensate for the fact that my @@DATEFIRST setting is 7, the default. 
    DECLARE @offSet int, @testDate datetime
    SELECT @offSet = 1, @testDate = GETDATE()
    
    SELECT CONVERT(DATETIME, CONVERT(INT, @testDate) - (DATEPART(WEEKDAY, @testDate) - @offSet))
    

    【讨论】:

    • 我发现这对我不起作用。我的 @@DATEFIRST 也是 7,但如果您的 @testDate 是一周的开始,那么这将返回前一天的日期。
    【解决方案6】:

    也许我在这里过于简化了,可能是这样,但这似乎对我有用。还没有遇到任何问题...

    CAST('1/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30)) AS DATETIME) + (DATEPART(wk, YOUR_DATE) * 7 - 7) as 'FirstDayOfWeek'
    CAST('1/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30)) AS DATETIME) + (DATEPART(wk, YOUR_DATE) * 7) as 'LastDayOfWeek'
    

    【讨论】:

    • 如果您尝试为SET DATEFIRST 设置不同的设置,您会在此处得到不同的答案。
    • 好吧,我没有投反对票,但你的回答根本没有提到DATEFIRST(现在已经三年半了),现在仍然没有。而且您应该避免使用像m/d/y 这样的区域格式,即使在 m 和 d 相同的情况下也是如此。
    【解决方案7】:

    我遇到了类似的问题。给定一个日期,我想获取该周的星期一的日期。

    我使用了以下逻辑:在 0-6 范围内找到一周中的天数,然后从 originay 日期中减去。

    我用过:DATEADD(day,-(DATEPART(weekday,)+5)%7,)

    由于 DATEPRRT(weekday,) 返回 1 = Sundaye ... 7=Saturday, DATEPART(weekday,)+5)%7 返回 0=Monday ... 6=Sunday。

    从原始日期减去这个天数得出上一个星期一。同样的技术可以用于一周的任何开始日期。

    【讨论】:

      【解决方案8】:
      创建函数 dbo.fnFirstWorkingDayOfTheWeek ( @currentDate 日期 ) 返回整数 作为 开始 -- 获取 DATEFIRST 设置 声明 @ds int = @@DATEFIRST -- 获取当前 DATEFIRST 设置下的星期几 声明@dow int = DATEPART(dw,@currentDate) DECLARE @wd int = 1+(((@dow+@ds) % 7)+5) % 7 -- 这总是返回 Mon 为 1,Tue 为 2 ... Sun 为 7 返回日期添加(dd,1-@wd,@currentDate) 结尾

      【讨论】:

      • 这是 SQL Server 2005 中唯一对我有用的函数。谢谢
      • @Fernando68 你能解释一下其他解决方案没有是如何工作的吗?
      • @AaronBertrand 抱歉不记得了,但我想我专注于快速回答,我尝试了你的回答,但由于某种原因它对我不起作用。
      • @Fernando68 嗯,这很有帮助。 :-\
      【解决方案9】:

      对于那些需要获取的:

      星期一 = 1 和星期日 = 7:

      SELECT 1 + ((5 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
      

      星期日 = 1 和星期六 = 7:

      SELECT 1 + ((6 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
      

      上面有一个类似的例子,但是多亏了双“%7”,它会慢得多。

      【讨论】:

      • 这对于从周日或周一的一周开始获取天数也很有效。谢谢
      • 或者select (datediff(dd,5,cal.D_DATE)%7 + 1)select (datediff(dd,6,cal.D_DATE)%7 + 1)
      【解决方案10】:

      由于 Julian 日期 0 是星期一,只需将周数添加到星期日 这是-1的前一天。选择 dateadd(wk,datediff(wk,0,getdate()),-1)

      【讨论】:

      • 就是这样兄弟!为什么总是使用广泛的逻辑、变量、函数使一切复杂化……?由于 1900 年 1 月 1 日是星期一,因此接下来的每个星期都将从星期一开始。所以如果你总是得到一个星期一的结果,但你想要一个星期天......只需减去 1 天!就这么简单!开心点;)
      【解决方案11】:

      我发现这既简单又有用。即使一周的第一天是周日或周一也能正常工作。

      将@BaseDate 声明为日期

      SET @BaseDate = GETDATE()

      将@FisrtDOW 声明为日期

      选择@FirstDOW = DATEADD(d,DATEPART(WEEKDAY,@BaseDate) *-1 + 1, @BaseDate)

      【讨论】:

        【解决方案12】:

        对于那些在工作中需要答案并且您的 DBA 禁止创建函数的人,以下解决方案将起作用:

        select *,
        cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-1), YourDate) as DATE) as WeekStart
        From.....
        

        这给出了那一周的开始。在这里,我假设星期日是几周的开始。如果你认为星期一是开始,你应该使用:

        select *,
        cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-2), YourDate) as DATE) as WeekStart
        From.....
        

        【讨论】:

          【解决方案13】:
          Set DateFirst 1;
          
          Select 
              Datepart(wk, TimeByDay) [Week]
              ,Dateadd(d,
                          CASE 
                          WHEN  Datepart(dw, TimeByDay) = 1 then 0
                          WHEN  Datepart(dw, TimeByDay) = 2 then -1
                          WHEN  Datepart(dw, TimeByDay) = 3 then -2
                          WHEN  Datepart(dw, TimeByDay) = 4 then -3
                          WHEN  Datepart(dw, TimeByDay) = 5 then -4
                          WHEN  Datepart(dw, TimeByDay) = 6 then -5
                          WHEN  Datepart(dw, TimeByDay) = 7 then -6
                          END
                          , TimeByDay) as StartOfWeek
          
          from TimeByDay_Tbl
          

          这是我的逻辑。将一周的第一天设置为星期一,然后计算给定日期是星期几,然后使用 DateAdd 和 Case 我计算该星期的前一个星期一的日期。

          【讨论】:

            【解决方案14】:

            对于基本(本周的星期日)

            select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) as date)
            

            如果是前一周:

            select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) -7 as date)
            

            在内部,我们构建了一个函数来执行此操作,但如果您需要快速和肮脏,这将执行此操作。

            【讨论】:

              【解决方案15】:

              如果您希望星期一作为一周的开始,我发现其他一些答案冗长或实际上不起作用。

              星期日

              SELECT DATEADD(week, DATEDIFF(week, -1, GETDATE()), -1) AS Sunday;
              

              星期一

              SELECT DATEADD(week, DATEDIFF(week, 0, GETDATE() - 1), 0) AS Monday;
              

              【讨论】:

                【解决方案16】:

                这对我很有用

                /* MeRrais 211126 
                select [dbo].[SinceWeeks](0,NULL)
                select [dbo].[SinceWeeks](5,'2021-08-31')
                */
                alter Function [dbo].[SinceWeeks](@Weeks int, @From datetime=NULL)
                Returns date
                AS
                Begin   
                
                    if @From is null 
                        set @From=getdate()
                
                    return cast(dateadd(day, -(@Weeks*7+datepart(dw,@From)-1), @From) as date)
                END
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2017-12-29
                  • 2020-06-04
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-26
                  • 2012-11-22
                  相关资源
                  最近更新 更多