获取SQL Server中的第一天

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)


Gan*_*rez 5

也许你需要这个:

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)

  • `DATEPART(DW` 依赖于 `@@datefirst` (6认同)
  • 为什么不直接设置输入参数“DATE”,那么您就不必对“VARCHAR”进行任何次优转换并返回,只是为了去除传入的任何意外时间部分。 (2认同)

tra*_*max 5

这对我来说非常有用:

创建功能[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)

结束