在 SQL Server 中获取一周的第一天

Posted

技术标签:

【中文标题】在 SQL Server 中获取一周的第一天【英文标题】:Get first day of week in 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。 【参考方案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

【讨论】:

以上是关于在 SQL Server 中获取一周的第一天的主要内容,如果未能解决你的问题,请参考以下文章

在objective-c中获取一周的第一天和最后一天

给定周数,返回 T-SQL 中一周的第一天

获取 Pandas 系列的一周的第一天

仅获取从一周的第一天到一周的当前日期的数据

来自 HTML5 Intl API 的一周的第一天

如何在Oracle中生成一周的第一天,一周的最后一天和两个日期之间的周数