我试图按周分组记录,将汇总日期存储为一周的第一天。但是,我用来舍入日期的标准技术似乎在几周内没有正常工作(尽管它确实存在了几天,几个月,几年,几个季度以及我应用它的任何其他时间框架。)
这是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);
但是我必须做出这样的例外让我有点不安。此外,如果这是一个重复的问题,请道歉。我发现了一些相关问题,但没有一个专门解决这个问题。
答案 0 :(得分:131)
回答为什么你会在星期一而不是星期天:
您在日期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');
回答如何获得星期天:
如果你想要一个星期天,那么选择一个不是星期一而是星期天的基准日期。例如:
DECLARE @dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);
如果您更改DATEFIRST
设置(或者您的代码针对具有不同设置的用户运行),则不会中断 - 前提是您仍然需要星期日,而不管当前设置如何。如果您希望这两个答案都是jive,那么您应该使用 依赖于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在高端出现最快,我感到有些惊讶。也许我会进行一些更全面的测试和博客...如果你们对我在其他地方发布你的功能没有任何异议。
答案 1 :(得分:14)
对于需要获得的这些:
星期一= 1,星期日= 7:
SELECT 1 + ((5 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
星期日= 1和星期六= 7:
SELECT 1 + ((6 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
上面有一个类似的例子,但是由于双重“%7”,它会慢得多。
答案 2 :(得分:4)
这对我来说非常有用:
CREATE FUNCTION [dbo].[StartOfWeek] ( @INPUTDATE DATETIME ) RETURNS DATETIME AS BEGIN -- THIS does not work in function. -- SET DATEFIRST 1 -- set monday to be the first day of week. DECLARE @DOW INT -- to store day of week SET @INPUTDATE = CONVERT(VARCHAR(10), @INPUTDATE, 111) SET @DOW = DATEPART(DW, @INPUTDATE) -- Magic convertion of monday to 1, tuesday to 2, etc. -- irrespect what SQL server thinks about start of the week. -- But here we have sunday marked as 0, but we fix this later. SET @DOW = (@DOW + @@DATEFIRST - 1) %7 IF @DOW = 0 SET @DOW = 7 -- fix for sunday RETURN DATEADD(DD, 1 - @DOW,@INPUTDATE) END
答案 3 :(得分:3)
用Google搜索此脚本:
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
答案 4 :(得分:3)
对于那些在工作中需要答案并且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.....
答案 5 :(得分: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
答案 6 :(得分:2)
CREATE FUNCTION dbo.fnFirstWorkingDayOfTheWeek ( @currentDate date ) RETURNS INT AS BEGIN -- get DATEFIRST setting DECLARE @ds int = @@DATEFIRST -- get week day number under current DATEFIRST setting DECLARE @dow int = DATEPART(dw,@currentDate) DECLARE @wd int = 1+(((@dow+@ds) % 7)+5) % 7 -- this is always return Mon as 1,Tue as 2 ... Sun as 7 RETURN DATEADD(dd,1-@wd,@currentDate) END
答案 7 :(得分:0)
由于朱利安日期0是星期一,所以只需将周数添加到星期日 这是前一天-1例如。选择dateadd(wk,datediff(wk,0,getdate()), - 1)
答案 8 :(得分:0)
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 I计算该星期一的星期一的日期。
答案 9 :(得分:0)
基本(当前周的星期日)
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) as date)
如果是上周:
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) -7 as date)
内部,我们构建了一个函数来执行此操作,但是如果您需要快速又脏的操作,则可以执行此操作。
答案 10 :(得分:-1)
我对这里给出的任何答案都没有任何问题,但是我认为我的实施和理解要简单得多。我没有对它进行任何性能测试,但它应该是可以忽略的。
所以我从日期作为整数存储在SQL服务器中的事实得出了答案(我只讨论日期组件)。如果您不相信我,请尝试使用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))
答案 11 :(得分:-1)
我有类似的问题。鉴于约会,我想得到那个星期一的星期日。
我使用了以下逻辑:在0-6范围内找到一周中的日期编号,然后从原始日期中减去该数字。
我用过:DATEADD(白天, - (DATEPART(工作日)+ 5)%7,)
由于DATEPRRT(工作日)返回1 =星期日... 7 =星期六, DATEPART(工作日,)+ 5)%7返回0 =星期一... 6 =星期日。
从原始日期减去这个天数给出了上一个星期一。同一技术可用于一周的任何开始日。
答案 12 :(得分:-1)
我发现这很简单有用。即使一周的第一天是星期日或星期一,也能正常工作。
宣布@BaseDate AS日期
SET @BaseDate = GETDATE()
DECLARE @FisrtDOW AS Date
SELECT @FirstDOW = DATEADD(d,DATEPART(WEEKDAY,@ BaseDate)* -1 + 1,@ BaseDate)
答案 13 :(得分:-3)
也许我在这里过度简化,情况可能如此,但这似乎对我有用。还没有遇到任何问题...
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'