2011-08-23 24 views
74

我試圖按星期對記錄進行分組,將聚合日期存儲爲一週中的第一天。然而,我用於四捨五入日期的標準技術似乎在幾周內不能正常工作(儘管它可以持續幾天,幾個月,幾年,幾個季度以及我已經應用到的任何其他時間範圍)。在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); 

但事實證明,我不得不做出這樣的例外,讓我有點不安。此外,如果這是一個重複的問題,道歉。我發現了一些相關的問題,但沒有一個具體解決這個問題。

+8

'(@@ DATEFIRST + DATEPART(DW,@SomeDate))%7'保持不變,而不考慮@@ datefirst設置。星期一= 2. –

回答

116

要回答爲什麼你得到一個星期一,而不是星期日:

你加入了數週的日期0.什麼是日期0? 1900-01-01。 1900-01-01的那一天是什麼?星期一。所以在你的代碼中,你說的是,自1900年1月1日星期一以來已經過去了多少個星期?我們稱之爲[n]。好吧,現在在1900年1月1日星期一加上[n]個星期。你不應該感到驚訝,因爲這個星期一結束了。 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以最快的速度出現在高端市場,我有點驚訝。也許我會運行一些更徹底的測試和博客關於它......如果你們不反對我在其他地方發佈你的功能。

+0

因此,如果我認爲我的周要在週日開始並在週六結束,那麼我可以在每週的_last_日期中獲取任何日期@d,如下所示:SELECT DATEADD(wk,DATEDIFF(wk,'19041231',@d) ,'19041231') – Baodad

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 

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47307

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 
+5

'DATEPART(DW'依賴於'@@ datefirst' –

+0

@馬丁史密斯頭腦清醒! – Gandarez

+0

我喜歡這個簡單性,它對於非常大的數據集似乎也運行得非常好。 –

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 
+0

從今天的日期開始,而不是星期天,這似乎是星期一返回.OP已經有一個函數返回星期一,他希望它返回星期日。:-) –

+0

d哦!下次我應該更仔細地閱讀問題。但是,如果仍然需要,我的解決方案可以輕鬆調整。似乎OP很滿意接受的答案 - ) – trailmax

+0

這是我的機器上的正確解決方案,因爲對我來說:DATEADD(ww,DATEDIFF(ww,0,CONVERT(DATE,'2017-10-8')), 0)返回2017-10-9! –

-1

我對這裏給出的答案沒有任何問題,但我確實認爲我的實現和理解要簡單得多。我沒有運行任何性能測試,但它應該是neglegable。

所以我從日期作爲整數存儲在SQL Server中(我只談論日期組件)這一事實推導出我的答案。如果你不相信我,試試這個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)) 
+1

我發現這對我不起作用。我的'@@ DATEFIRST'也是7,但如果你的'@ testDate'是一週的開始,那麼這將返回一個前一天的日期。 – row1

-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' 
+0

如果您爲'SET DATEFIRST'嘗試不同的設置,您可以在這裏得到不同的答案。 –

+0

Just SET DATEFIRST 1,問題解決了。 – mark

+5

好吧,我沒有投票,但你的答案根本沒有提及'DATEFIRST'(現在三年半),但仍然沒有。即使在m和d相同的情況下,你也應該*避免使用像'm/d/y'這樣的區域格式。 –

-1

我也有類似的問題。給定一個日期,我想得到那個星期一的日期。

我使用了以下邏輯:在0-6的範圍內查找星期中的日數,然後從原始日期中減去該日數。

我使用:DATEADD(天, - (DATEPART(星期,)+ 5)%7,)

由於DATEPRRT(星期,)返回1 = Sundaye ... 7 =星期六, DATEPART(平日,)+ 5)%7返回0 =星期一... 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+(((@[email protected]) % 7)+5) % 7 -- this is always return Mon as 1,Tue as 2 ... Sun as 7 

    RETURN DATEADD(dd,[email protected],@currentDate) 

END 
+0

這是SQL Server 2005中唯一適用於我的函數。謝謝 – Fandango68

+0

@ Fernando68您能否解釋其他解決方案*如何不工作? –

+0

@AaronBertrand抱歉不記得了,但我認爲我正在專注於快速回答,並且我嘗試過你,但由於某種原因,它對我沒有任何作用。 – Fandango68

10

對於這些需要得到:

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

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

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

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

上面有是一個類似的例子,但是由於雙「%7」,它會慢得多。

+0

這個工程也非常有用,可以讓星期幾開始的日子是Sun或Mon.謝謝 – Fandango68

+0

或者'select(datediff(dd,5,cal.D_DATE)%7 + 1)'和'select(datediff(dd,6,cal.D_DATE)%7 + 1)'' – vasja

0

由於Julian日期0是星期一隻需加上星期日數 即前一天-1例如。 select dateadd(wk,datediff(wk,0,getdate()), - 1)

-1

我發現這個簡單而有用。即使每週的第一天是星期日或星期一也可以工作。

DECLARE @BaseDate AS日期

SET @BaseDate = GETDATE()

DECLARE @FisrtDOW AS日期

SELECT @FirstDOW = DATEADD(d,DATEPART(星期,@ BaseDate)* - 1 @ 1,@BaseDate)

相關問題