在 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 中获取一周的第一天的主要内容,如果未能解决你的问题,请参考以下文章