web-dev-qa-db-ja.com

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);

しかし、そのような例外を作らなければならないという事実は、私を少し不安にさせます。また、これが重複した質問である場合はおologiesび申し上げます。関連する質問をいくつか見つけましたが、この側面を具体的に扱った質問はありませんでした。

88
Quick Joe Smith

日曜日ではなく月曜日になる理由に答えるには:

日付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設定を変更しても(または、設定が異なるユーザーに対してコードが実行されていても)、これは中断しません-現在の設定に関係なく日曜日が必要な場合。これら2つの答えをjiveにしたい場合は、DATEFIRST設定に依存するdoes関数を使用する必要があります。

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

さて、あなたは多くの選択肢を持っていますが、どれが最も効果的ですか?大きな違いがあるかどうかは驚きですが、これまでに提供されたすべての回答を収集し、2つのテストセット(安価なものと高価なもの)を実行しました。ここでパフォーマンスに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がハイエンドで最速として出てくるのを見て驚いた。もしかしたら、他の場所で関数を公開することに異論がないなら、もっと徹底的なテストとブログをするでしょう。

134
Aaron Bertrand

これらを取得する必要がある場合:

月曜日= 1および日曜日= 7:

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

日曜日= 1および土曜日= 7:

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

上記にも同様の例がありましたが、二重の「%7」のおかげではるかに遅くなります。

15
Kakkarot

これは私にとって素晴らしい仕事です:

作成関数[dbo]。[StartOfWeek] 
(
 @INPUTDATE DATETIME 
)
 RETURNS DATETIME 
 
 AS 
 BEGIN 
-これは機能しません。
-SET DATEFIRST 1-月曜日を週の最初の日に設定します。
 
 DECLARE @DOW INT-曜日を保存します
 SET @INPUTDATE = CONVERT(VARCHAR(10)、@INPUTDATE、111)
 SET @DOW = DATEPART(DW、@ INPUTDATE)
 
-月曜日を1に、火曜日を2に、などに魔法に変換します。
-SQLサーバーが週の始まりについて何を考えているかを無視します。 -しかし、ここでは日曜日を0としてマークしていますが、後で修正します。
 SET @DOW =(@DOW + @@ DATEFIRST-1)%7 
 IF @DOW = 0 SET @ DOW = 7-日曜日の修正
 
 RETURN DATEADD(DD、1-@ DOW、@ INPUTDATE)
 
 END 
5
trailmax

仕事で答えが必要であり、作成機能が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.....
4
Philip Chen

このスクリプトを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

3
Curt

たぶんあなたはこれが必要です:

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
2
Gandarez
 CREATE FUNCTION dbo.fnFirstWorkingDayOfTheWeek 
(
 @currentDate date 
)
 RETURNS INT 
 AS 
 BEGIN 
-DATEFIRST設定を取得
 DECLARE @ds int = @@ DATEFIRST 
-現在のDATEFIRST設定の下で曜日番号を取得
 DECLARE @dow int = DATEPART(dw 、@ currentDate)
 
 DECLARE @wd int = 1 +(((@ dow + @ ds)%7)+5)%7-これは常にMonを1、Tueを2として返します... Sun as 7 
 
 RETURN DATEADD(dd、1- @ wd、@ currentDate)
 
 END 
2
JG JIN

ユリウス日0は月曜日なので、-1の前日である日曜日に週数を追加するだけです。選択dateadd(wk、datediff(wk、0、getdate())、-1)

0
Iggy
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を使用して、その週の前の月曜日に何が行われるかを計算します。

0
user2479728

基本(現在の週の日曜日)

select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) as date)

前週の場合:

select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) -7 as date)

内部的には、それを行う関数を作成しましたが、迅速で汚れたものが必要な場合は、これを実行します。

0
JamesDavisSr