Qui*_*ith 88 t-sql sql-server date sql-server-2008
我试图按周分组记录,将汇总日期存储为一周的第一天.但是,我用于舍入日期的标准技术似乎在几周内没有正常工作(尽管它确实存在了几天,几个月,几年,几个季度以及我应用它的任何其他时间范围).
这是SQL:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);
Run Code Online (Sandbox Code Playgroud)
返回2011-08-22 00:00:00.000
,这是星期一,而不是星期日.选择@@datefirst
返回7
,这是星期日的代码,所以据我所知,服务器设置正确.
通过将上面的代码更改为:我可以轻松地绕过这个:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);
Run Code Online (Sandbox Code Playgroud)
但事实上,我必须做出这样的例外让我有点不安.此外,如果这是一个重复的问题,请道歉.我发现了一些相关问题,但没有一个专门解决这个问题.
Aar*_*and 138
要回答为什么你得到星期一而不是星期天:
您在日期0添加了几周.什么是日期0?1900-01-01.1900-01-01的那一天是什么日子?星期一.所以在您的代码中,您说,自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');
Run Code Online (Sandbox Code Playgroud)
要回答如何获得星期天:
如果你想要一个星期天,那么选择一个不是星期一而是星期天的基准日期.例如:
DECLARE @dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);
Run Code Online (Sandbox Code Playgroud)
如果您更改DATEFIRST
设置(或者您的代码针对具有不同设置的用户运行),这不会中断- 前提是您仍然需要星期日,而不管当前设置如何.如果您希望这两个答案,JIVE,那么你应该使用一个功能不依赖于DATEFIRST
设置,例如,
SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
Run Code Online (Sandbox Code Playgroud)
因此,如果您将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
Run Code Online (Sandbox Code Playgroud)
...要么...
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
Run Code Online (Sandbox Code Playgroud)
现在,你有很多选择,但哪一个表现最好?如果会有任何重大差异,我会感到惊讶,但我收集到目前为止提供的所有答案,并通过两组测试运行 - 一个便宜,一个昂贵.我测量了客户端统计信息,因为我没有看到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
Run Code Online (Sandbox Code Playgroud)
"昂贵的"分配查询:
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
Run Code Online (Sandbox Code Playgroud)
如果需要,我可以传递我的测试细节 - 在这里停下来,因为这已经变得非常冗长.考虑到计算和内联代码的数量,看到Curt在高端出现最快,我感到有些惊讶.也许我会进行一些更彻底的测试和博客...如果你们对我在其他地方发布你的功能没有任何异议.
小智 16
对于需要获得的这些:
星期一= 1,星期日= 7:
SELECT 1 + ((5 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
Run Code Online (Sandbox Code Playgroud)
星期日= 1和星期六= 7:
SELECT 1 + ((6 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
Run Code Online (Sandbox Code Playgroud)
上面有一个类似的例子,但由于双"%7",它会慢得多.
小智 6
对于那些在工作中需要答案并且创建功能被DBA禁止的人,以下解决方案将起作用:
select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-1), YourDate) as DATE) as WeekStart
From.....
Run Code Online (Sandbox Code Playgroud)
这是该周的开始。在这里,我假设星期日是几周的开始。如果您认为星期一是开始,则应使用:
select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-2), YourDate) as DATE) as WeekStart
From.....
Run Code Online (Sandbox Code Playgroud)
也许你需要这个:
SELECT DATEADD(DD, 1 - DATEPART(DW, GETDATE()), GETDATE())
Run Code Online (Sandbox Code Playgroud)
或者
DECLARE @MYDATE DATETIME
SET @MYDATE = '2011-08-23'
SELECT DATEADD(DD, 1 - DATEPART(DW, @MYDATE), @MYDATE)
Run Code Online (Sandbox Code Playgroud)
功能
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
Run Code Online (Sandbox Code Playgroud)
这对我来说非常有用:
创建功能[dbo]。[StartOfWeek] ( @INPUTDATE DATETIME ) 返回日期时间 如 开始 -此功能不起作用。 -SET DATEFIRST 1-将星期一设置为一周的第一天。 DECLARE @DOW INT-存储星期几 设置@INPUTDATE = CONVERT(VARCHAR(10),@INPUTDATE,111) 设置@DOW = DATEPART(DW,@INPUTDATE) -星期一至1的魔术转换,星期二至2的依此类推。 -不理会SQL Server对一周开始的想法。 -但是在这里我们将星期日标记为0,但是我们稍后将其修复。 SET @DOW =(@DOW + @@ DATEFIRST-1)%7 如果@DOW = 0 SET @DOW = 7-修复星期日 RETURN DATEADD(DD,1-@ DOW,@ INPUTDATE) 结束