在SQL Server中获取第一个星期几

我试图按星期将logging分组,将汇总的date存储为一周中的第一天。 然而,我用四舍五入的date标准技术似乎没有正常工作几个星期(尽pipe它有几天,几个月,几年,季度和我已经应用它的任何其他时间表)。

这里是SQL:

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

这将返回2011-08-22 00:00:00.000 ,这是一个星期一,而不是星期天。 select@@datefirst返回7 ,这是星期天的代码,所以服务器安装正确,据我所知。

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

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

但是,我必须做出这样的例外,这让我有点不安。 此外,如果这是一个重复的问题,表示歉意。 我发现了一些相关的问题,但没有一个具体解决这个问题。

要回答你为什么得到星期一而不是星期天:

你正在添加几个星期的date0.什么是date0? 1900-01-01。 1900-01-01的那一天是什么? 星期一。 所以在你的代码中,你说1900年1月1日星期一以来已经过去了多less个星期? 我们称之为[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'); 

要回答如何获得星期天:

如果你想要一个星期天,那么select一个不是星期一而是星期天的基准date。 例如:

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

如果您更改了DATEFIRST设置(或者您的代码正在为具有不同设置的用户运行),则不会中断,前提是您仍然需要一个星期天,而不pipe当前设置如何。 如果你想要这两个jive的答案,那么你应该使用一个函数,取决于DATEFIRST设置,例如

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

所以如果你把你的DATEFIRST设置改成星期一,星期二,你有什么,这个行为会改变。 根据您想要的行为,您可以使用以下function之一:

 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 

现在,你有很多的select,但哪一个performance最好? 如果有什么重大分歧,我会感到惊讶,但我收集了迄今为止提供的所有答案,并通过两套testing – 一种便宜,一种昂贵的testing。 我测量了客户端的统计数据,因为我没有看到I / O或内存在这里的性能performance(尽pipe这些可能会根据function的使用而变化)。 在我的testing中,结果是:

“便宜”分配查询:

 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 

如果需要的话,我可以传达我的testing的细节 – 在这里停止,因为这已经变得相当冗长。 考虑到计算和内联代码的数量,看到Curt出现在最高端的速度最快,我有点惊讶。 也许我会运行一些更彻底的testing和博客…如果你们没有任何异议,我在其他地方发布你的function。

对于这些需要得到:

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

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

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

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

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

谷歌search这个脚本:

 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

这对我来说非常有用:

 CREATE FUNCTION [dbo]。[StartOfWeek]
 (
   @INPUTDATE DATETIME
 )
返回DATETIME

如
开始
   - 这不起作用。
   -  SET DATEFIRST 1  - 设置星期一为星期几的第一天。

   DECLARE @DOW INT  - 存储星期几
   SET @INPUTDATE = CONVERT(VARCHAR(10),@ INPUTDATE,111)
   SET @DOW = DATEPART(DW,@INPUTDATE)

   - 周一至周二,周二至周二的魔术转换
   - 不pipeSQL服务器认为一周的开始。
   - 但是在这里我们有星期天标记为0,但我们稍后修复。
   SET @DOW =(@DOW + @@ DATEFIRST  -  1)%7
   IF @DOW = 0 SET @DOW = 7  - 星期天修好

   RETURN DATEADD(DD,1  -  @ DOW,@ INPUTDATE)

结束

也许你需要这个:

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

要么

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

function

 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 
 CREATE FUNCTION dbo.fnFirstWorkingDayOfTheWeek
 (
     @currentDatedate
 )
返回INT
如
开始
     - 获得DATEFIRST设置
     DECLARE @ds int = @@ DATEFIRST 
     - 在当前的DATEFIRST设置下获得周日数
     DECLARE @dow int = DATEPART(dw,@ currentDate) 

     DECLARE @wd int = 1 +(((@ dow + @ ds)%7)+5)%7  - 总是返回Mon为1,Tue为2 ... Sun为7 

     RETURN DATEADD(dd,1- @ wd,@ currentDate) 

结束

由于Juliandate0是一个星期一,只需加上星期几到星期日,即前一天-1 Eg。 selectdateadd(wk,datediff(wk,0,getdate()), – 1)

我对这里给出的任何答案都没有任何问题,但是我认为我的实现和理解要简单得多。 我没有运行任何性能testing,但应该是neglegable。

所以我从date作为整数存储在SQL服务器中的事实(我只谈论date组件)推导出我的答案。 如果你不相信我,试试这个SELECT CONVERT(INT,GETDATE()),反之亦然。

现在知道这一点,你可以做一些很酷的math方程。 你可能会想出一个更好的,但这是我的。

 /* 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, US 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)) 

我有一个类似的问题。 给定一个date,我想得到那个星期一的date。

我使用了以下逻辑:在0-6的范围内查找一周中的日数,然后从原始date中减去。

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

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

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

我发现这个简单而有用。 即使星期一或星期一的第一天工作。

DECLARE @BaseDate AS Date

SET @BaseDate = GETDATE()

DECLARE @FisrtDOW AS Date

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

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

 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'