在 SQL Server 中获取一周的第一天
我试图按周对记录进行分组,将聚合日期存储为一周的第一天。然而,我用于四舍五入日期的标准技术似乎无法在几周内正常工作(尽管它可以在天、月、年、季度和我应用的任何其他时间范围内正常工作)。
以下是 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);
但我必须做出这样的例外这一事实让我有点不安。另外,如果这是一个重复的问题,我们深表歉意。我发现了一些相关的问题,但没有一个专门解决这方面的问题。
I am trying to group records by week, storing the aggregated date as the first day of the week. However, the standard technique I use for rounding off dates does not appear to work correctly with weeks (though it does for days, months, years, quarters and any other timeframe I've applied it to).
Here is the SQL:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);
This returns 2011-08-22 00:00:00.000
, which is a Monday, not a Sunday. Selecting @@datefirst
returns 7
, which is the code for Sunday, so the server is setup correctly in as far as I know.
I can bypass this easily enough by changing the above code to:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);
But the fact that I have to make such an exception makes me a little uneasy. Also, apologies if this is a duplicate question. I found some related questions but none that addressed this aspect specifically.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
要回答为什么是星期一而不是星期日:
您要在日期 0 的基础上添加周数。日期 0 是什么? 1900 年 1 月 1 日。 1900 年 1 月 1 日是什么日子?周一。所以在你的代码中你会说,自 1900 年 1 月 1 日星期一以来已经过去了多少周?我们称之为[n]。好的,现在将 [n] 周添加到 1900 年 1 月 1 日星期一。您不应该对这最终成为星期一感到惊讶。
DATEADD
不知道您要添加几周,但直到您到达星期日为止,它只是添加 7 天,然后再添加 7 天,...就像DATEDIFF
> 只识别已经跨越的界限。例如,尽管有些人抱怨应该内置一些合理的逻辑来向上或向下舍入,但它们都返回 1:要回答如何获得星期日:
如果您想要星期日,则选择一个不是星期一而是星期日的基准日期。例如:
如果您更改
DATEFIRST
设置(或者您的代码正在为具有不同设置的用户运行),则这不会中断 - 前提是无论当前设置如何,您仍然想要星期日。如果您想要 jive 这两个答案,那么您应该使用确实依赖于DATEFIRST
设置的函数,例如,如果您更改
DATEFIRST
> 设置为周一、周二,你有什么,行为就会改变。根据您想要的行为,您可以使用以下功能之一:...或者...
现在,您有很多替代方案,但哪一个性能最好?如果存在任何重大差异,我会感到惊讶,但我收集了迄今为止提供的所有答案,并对它们进行了两组测试 - 一组便宜,一组昂贵。我测量了客户端统计数据,因为我没有看到 I/O 或内存在性能中发挥作用(尽管这些可能会发挥作用,具体取决于函数的使用方式)。在我的测试中,结果是:
“便宜”的分配查询:
“昂贵”的分配查询:
如果需要,我可以转发我的测试的详细信息 - 在此停止,因为这已经变得相当冗长了。考虑到计算量和内联代码的数量,我有点惊讶地看到 Curt 成为高端最快的。也许我会进行一些更彻底的测试并在博客中介绍它......如果你们不反对我在其他地方发布您的函数。
To answer why you're getting a Monday and not a Sunday:
You're adding a number of weeks to the date 0. What is date 0? 1900-01-01. What was the day on 1900-01-01? Monday. So in your code you're saying, how many weeks have passed since Monday, January 1, 1900? Let's call that [n]. Ok, now add [n] weeks to Monday, January 1, 1900. You should not be surprised that this ends up being a Monday.
DATEADD
has no idea that you want to add weeks but only until you get to a Sunday, it's just adding 7 days, then adding 7 more days, ... just likeDATEDIFF
only recognizes boundaries that have been crossed. For example, these both return 1, even though some folks complain that there should be some sensible logic built in to round up or down:To answer how to get a Sunday:
If you want a Sunday, then pick a base date that's not a Monday but rather a Sunday. For example:
This will not break if you change your
DATEFIRST
setting (or your code is running for a user with a different setting) - provided that you still want a Sunday regardless of the current setting. If you want those two answers to jive, then you should use a function that does depend on theDATEFIRST
setting, e.g.So if you change your
DATEFIRST
setting to Monday, Tuesday, what have you, the behavior will change. Depending on which behavior you want, you could use one of these functions:...or...
Now, you have plenty of alternatives, but which one performs best? I'd be surprised if there would be any major differences but I collected all the answers provided so far and ran them through two sets of tests - one cheap and one expensive. I measured client statistics because I don't see I/O or memory playing a part in the performance here (though those may come into play depending on how the function is used). In my tests the results are:
"Cheap" assignment query:
"Expensive" assignment query:
I can relay the details of my tests if desired - stopping here as this is already getting quite long-winded. I was a bit surprised to see Curt's come out as the fastest at the high end, given the number of calculations and inline code. Maybe I'll run some more thorough tests and blog about it... if you guys don't have any objections to me publishing your functions elsewhere.
对于那些需要获取的:
星期一 = 1 和星期日 = 7:
星期日 = 1 和星期六 = 7:
上面有一个类似的例子,但由于双“%7”,它会慢得多。
For these that need to get:
Monday = 1 and Sunday = 7:
Sunday = 1 and Saturday = 7:
Above there was a similar example, but thanks to double "%7" it would be much slower.
对于那些在工作中需要答案并且 DBA 禁止创建函数的人,以下解决方案将起作用:
这给出了那一周的开始。在这里,我假设星期日是几周的开始。如果您认为星期一是开始,您应该使用:
For those who need the answer at work and creating function is forbidden by your DBA, the following solution will work:
This gives the start of that week. Here I assume that Sundays are the start of weeks. If you think that Monday is the start, you should use:
也许您需要这个:
或
功能
Maybe you need this:
Or
Function
这对我来说非常有效:
This works wonderfully for me:
谷歌搜索这个脚本:
http://www.sqlteam.com/forums/topic.asp ?TOPIC_ID=47307
Googled this script:
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47307
对于基本的(本周的星期日)
如果上周:
在内部,我们构建了一个可以执行此操作的函数,但如果您需要快速而肮脏的功能,这将可以实现。
For the basic (the current week's Sunday)
If previous week:
Internally, we built a function that does it but if you need quick and dirty, this will do it.
由于儒略日期 0 是星期一,只需将星期数添加到星期日
这是-1 的前一天,例如。选择 dateadd(wk,datediff(wk,0,getdate()),-1)
Since Julian date 0 is a Monday just add the number of weeks to Sunday
which is the day before -1 Eg. select dateadd(wk,datediff(wk,0,getdate()),-1)
我发现其他一些答案很冗长,或者如果您希望星期一作为一周的开始,则实际上并不起作用。
周日
周一
I found some of the other answers long-winded or didn't actually work if you wanted Monday as the start of the week.
Sunday
Monday
这是我的逻辑。将一周的第一天设置为星期一,然后计算给定的一天是星期几,然后使用 DateAdd 和 Case 我计算该日期是该周的前一个星期一。
This is my logic. Set the first of the week to be Monday then calculate what is the day of the week a give day is, then using DateAdd and Case I calculate what the date would have been on the previous Monday of that week.
这对我来说是一个有用的功能
This is a useful function for me
我对这里给出的任何答案都没有任何问题,但我确实认为我的答案更容易实现和理解。我还没有对其进行任何性能测试,但它应该可以忽略不计。
所以我从日期作为整数存储在 SQL Server 中这一事实得出了我的答案(我只讨论日期组件)。如果您不相信我,请尝试此 SELECT CONVERT(INT, GETDATE()),反之亦然。
现在知道了这一点,您可以做一些很酷的数学方程。你也许能想出更好的,但这是我的。
I don't have any issues with any of the answers given here, however I do think mine is a lot simpler to implement, and understand. I have not run any performance tests on it, but it should be neglegable.
So I derived my answer from the fact that dates are stored in SQL server as integers, (I am talking about the date component only). If you don't believe me, try this SELECT CONVERT(INT, GETDATE()), and vice versa.
Now knowing this, you can do some cool math equations. You might be able to come up with a better one, but here is mine.
我有类似的问题。给定一个日期,我想获取该周星期一的日期。
我使用了以下逻辑:找到一周中 0-6 范围内的天数,然后从原始日期中减去该天数。
我使用: DATEADD(day,-(DATEPART(weekday,)+5)%7,)
由于 DATEPRRT(weekday,) 返回 1 = Sundaye ... 7=星期六,
DATEPART(weekday,)+5)%7 返回 0=星期一 ... 6=星期日。
从原始日期减去此天数得出上周一。相同的技术可用于一周中的任何开始日。
I had a similar problem. Given a date, I wanted to get the date of the Monday of that week.
I used the following logic: Find the day number in the week in the range of 0-6, then subtract that from the originay date.
I used: DATEADD(day,-(DATEPART(weekday,)+5)%7,)
Since DATEPRRT(weekday,) returns 1 = Sundaye ... 7=Saturday,
DATEPART(weekday,)+5)%7 returns 0=Monday ... 6=Sunday.
Subtracting this number of days from the original date gives the previous Monday. The same technique could be used for any starting day of the week.
我发现这简单又有用。即使一周的第一天是星期日或星期一也有效。
声明 @BaseDate AS 日期
SET @BaseDate = GETDATE()
声明 @FisrtDOW AS 日期
SELECT @FirstDOW = DATEADD(d,DATEPART(WEEKDAY,@BaseDate) *-1 + 1, @BaseDate)
I found this simple and usefull. Works even if first day of week is Sunday or Monday.
DECLARE @BaseDate AS Date
SET @BaseDate = GETDATE()
DECLARE @FisrtDOW AS Date
SELECT @FirstDOW = DATEADD(d,DATEPART(WEEKDAY,@BaseDate) *-1 + 1, @BaseDate)
也许我在这里过于简化了,情况可能就是这样,但这似乎对我有用。目前还没有遇到任何问题...
Maybe I'm over simplifying here, and that may be the case, but this seems to work for me. Haven't ran into any problems with it yet...