首页 文章

获取SQL Server中的第一天

提问于
浏览
83

我试图按周分组记录,将汇总日期存储为一周的第一天 . 但是,我用于舍入日期的标准技术似乎在几周内没有正常工作(尽管它确实存在了几天,几个月,几年,几个季度以及我应用它的任何其他时间范围) .

这是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);

但事实上,我必须做出这样的例外让我有点不安 . 此外,如果这是一个重复的问题,请道歉 . 我发现了一些相关问题,但没有一个专门解决这个问题 .

13 回答

  • -3

    To answer why you're getting a Monday and not a Sunday:

    你说,自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');
    

    To answer how to get a Sunday:

    如果你想要一个星期天,那么选择一个不是星期一而是星期天的基准日期 . 例如:

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

    如果您更改 DATEFIRST 设置(或者您的代码正在为具有不同设置的用户运行),这不会中断 - 前提是您仍然需要星期日,而不管当前设置如何 . 如果你想要这两个jive的答案,那么你应该使用一个依赖于 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在高端出现最快,我感到有些惊讶 . 也许我会进行一些更彻底的测试和博客...如果你们对我在其他地方发布你的功能没有任何异议 .

  • -1

    对于需要获得的这些:

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

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

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

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

    上面有一个类似的例子,但由于双“%7”,它会慢得多 .

  • 0

    这对我很有用:

    CREATE FUNCTION [dbo].[StartOfWeek]
    (
      @INPUTDATE DATETIME
    )
    RETURNS DATETIME
    
    AS
    BEGIN
      -- THIS does not work in function.
      -- SET DATEFIRST 1 -- set monday to be the first day of week.
    
      DECLARE @DOW INT -- to store day of week
      SET @INPUTDATE = CONVERT(VARCHAR(10), @INPUTDATE, 111)
      SET @DOW = DATEPART(DW, @INPUTDATE)
    
      -- Magic convertion of monday to 1, tuesday to 2, etc.
      -- irrespect what SQL server thinks about start of the week.
      -- But here we have sunday marked as 0, but we fix this later.
      SET @DOW = (@DOW + @@DATEFIRST - 1) %7
      IF @DOW = 0 SET @DOW = 7 -- fix for sunday
    
      RETURN DATEADD(DD, 1 - @DOW,@INPUTDATE)
    
    END
    
  • -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

    对于那些需要在工作中得到答案并且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.....
    
  • 3

    也许你需要这个:

    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
    
  • 3
    CREATE FUNCTION dbo.fnFirstWorkingDayOfTheWeek
    (
        @currentDate date
    )
    RETURNS INT
    AS
    BEGIN
        -- get DATEFIRST setting
        DECLARE @ds int = @@DATEFIRST 
        -- get week day number under current DATEFIRST setting
        DECLARE @dow int = DATEPART(dw,@currentDate) 
    
        DECLARE @wd  int =  1+(((@dow+@ds) % 7)+5) % 7  -- this is always return Mon as 1,Tue as 2 ... Sun as 7 
    
        RETURN DATEADD(dd,1-@wd,@currentDate) 
    
    END
    
  • 0

    由于朱利安日期0是星期一,所以只需将星期数添加到星期日,即-1 Eg之前的一天 . 选择dateadd(wk,datediff(wk,0,getdate()), - 1)

  • -1
    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 I计算该星期一周一的日期 .

  • 129

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

    所以我从日期作为整数存储在SQL服务器中的事实得出了我的答案,(我只讨论日期组件) . 如果你不这样做相信我,尝试这个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))
    
  • 4

    我遇到了类似的问题 . 鉴于约会,我想得到那个星期一的星期日 .

    我使用了以下逻辑:在0-6范围内找到一周中的日期编号,然后从原始日期中减去该数字 .

    我用过:DATEADD(白天, - (DATEPART(工作日))5%7)

    由于DATEPRRT(工作日)返回1 =星期日... 7 =星期六,DATEPART(工作日,)5)%7返回0 =星期一... 6 =星期日 .

    从原始日期减去这个天数给出了上一个星期一 . 一周的任何开始日都可以使用相同的技术 .

  • 2

    我发现这很简单有用 . 即使一周的第一天是星期日或星期一也能正常工作 .

    DECLARE @BaseDate AS日期

    SET @BaseDate = GETDATE()

    DECLARE @FisrtDOW AS日期

    SELECT @FirstDOW = DATEADD(d,DATEPART(WEEKDAY,@ BaseDate)* -1 1,@ BaseDate)

  • 13

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

    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'
    

相关问题