SQL Server에서 요일 가져 오기


97

집계 된 날짜를 요일로 저장하여 레코드를 주별로 그룹화하려고합니다. 그러나 날짜를 반올림하는 데 사용하는 표준 기술은 주 단위로 올바르게 작동하지 않는 것 같습니다 (일, 월, 년, 분기 및 내가 적용한 다른 기간에는 적용됨).

다음은 SQL입니다.

select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);

이것은 2011-08-22 00:00:00.000일요일이 아니라 월요일을 반환합니다 . 일요일의 코드 인 @@datefirstreturns를 선택하면 7내가 아는 한 서버가 올바르게 설정됩니다.

위의 코드를 다음과 같이 변경하여 쉽게 우회 할 수 있습니다.

select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);

하지만 그런 예외를 만들어야한다는 사실이 조금 불안해합니다. 또한 중복 질문 인 경우 사과드립니다. 몇 가지 관련 질문을 찾았지만이 측면을 구체적으로 다루는 질문은 없습니다.


9
(@@DATEFIRST + DATEPART(DW, @SomeDate)) % 7@@datefirst내가 생각 하는 설정에 관계없이 일정하게 유지됩니다 . 월요일 = 2
Martin Smith

답변:


150

일요일이 아닌 월요일이되는 이유에 대한 답변 :

날짜 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현재 설정에 관계없이 여전히 일요일을 원하는 경우 설정 을 변경 하거나 코드가 다른 설정을 가진 사용자를 위해 실행되는 경우에도 중단되지 않습니다 . 당신이 흔들로 두 답변을 원하는 경우에, 당신은 함수 사용해야 않는 에 따라 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가 최고급에서 가장 빠르게 나오는 것을보고 조금 놀랐습니다. 아마도 좀 더 철저한 테스트를 실행하고 그것에 대해 블로그를 할 것입니다. 만약 여러분이 다른 곳에 여러분의 함수를 게시하는 것에 반대하지 않는다면.


그래서, 내가 얻을 수, 토요일에 일요일 끝에 시작하는 내 주 고려하는 경우 마지막으로 이런 모든 날짜 @d에 대한 요일 : (SELECT DATEADD WK, DATEDIFF (WK, '19041231', @d) '19041231')
Baodad

21

필요한 항목 :

월요일 = 1, 일요일 = 7 :

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

일요일 = 1, 토요일 = 7 :

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

위에 비슷한 예가 있었지만 "% 7"두 배 덕분에 훨씬 느려질 것입니다.


이것은 일요일 또는 월요일이 시작되는 요일 번호를 얻는데도 효과적입니다. 감사합니다
Fandango68

또한 select (datediff(dd,5,cal.D_DATE)%7 + 1)select (datediff(dd,6,cal.D_DATE)%7 + 1)
vasja

8

직장에서 답변이 필요하고 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

이것은 나를 위해 훌륭하게 작동합니다.

CREATE FUNCTION [dbo]. [StartOfWeek]
(
  @INPUTDATE DATETIME
)
DATETIME 반환

같이
시작
  -이 기능은 작동하지 않습니다.
  -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
  @DOW = 0 SET @DOW = 7이면 일요일 수정

  반환 날짜 ADD (DD, 1-@ DOW, @ INPUTDATE)

종료

이것은 일요일이 아니라 오늘 날짜가 주어진 월요일을 반환하는 것 같습니다. OP에는 이미 월요일을 반환하는 함수가 있으며 일요일을 반환하기를 원합니다. :-)
Aaron Bertrand

도! 다음에 질문을 좀 더주의 깊게 읽어야합니다. 그러나 여전히 필요한 경우 내 솔루션을 쉽게 조정할 수 있습니다. 어쨌든 OP는 받아 들인 대답에 만족하는 것 같습니다-)
trailmax

이것은 내 컴퓨터에서 올바른 솔루션입니다.
CMD 실행

3

이 스크립트를 검색했습니다.

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

6
DATEPART(DW에 의존@@datefirst
Martin Smith

나는 이것의 단순함을 좋아한다. 매우 큰 데이터 세트에서도 꽤 잘 실행되는 것 같습니다.
Quick Joe Smith

2
뿐만 아니라 입력 매개 변수를 만들기 왜 DATE당신은 어떤 차선의 변환을 할 필요가 없습니다 VARCHAR다시 그냥 통과 상관 없음 우발적 인 시간 구성 요소를 제거 할 수 있습니다.
아론 버트 랜드

반환 된 값에 값이 필요하지 않기 때문에 Convert 함수가 사용되었습니다 Time.
Gandarez 2011 년

1
예,하지만 요점은 varchar로 다시 변환하는 것은 비용이 많이 든다는 것입니다. DATE 매개 변수 만 있으면 시간이 포함되었는지 여부는 신경 쓰지 않습니다.
Aaron Bertrand

2
함수 생성 dbo.fnFirstWorkingDayOfTheWeek
(
    @currentDate 날짜
)
INT 반환
같이
시작
    -DATEFIRST 설정 얻기
    @ds int = @@ DATEFIRST 선언 
    -현재 DATEFIRST 설정에서 요일 번호 가져 오기
    @dow int = DATEPART (dw, @ currentDate) 선언 

    DECLARE @wd int = 1 + (((@ dow + @ ds) % 7) +5) % 7-항상 Mon을 1로, Tue를 2로 ... Sun을 7로 반환합니다. 

    반환 날짜 ADD (dd, 1- @ wd, @ currentDate) 

종료

이는 2005 년 당신을 감사 SQL 서버에서 나를 위해 일한 유일한 기능입니다
Fandango68

@ Fernando68 다른 솔루션 어떻게 작동 하지 않았 는지 설명 할 수 있습니까 ?
Aaron Bertrand

@AaronBertrand 죄송합니다 기억하지 못하지만 빠른 답변에 초점을 맞추고 있다고 생각하고 귀하의 답변을 시도했지만 어떤 이유로 든 작동하지 않았습니다.
Fandango68

@ Fernando68 글쎄, 그것은 매우 유용합니다. :-\
Aaron Bertrand

2

기본 용 (금주의 일요일)

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

지난 주 :

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

내부적으로 우리는이를 수행하는 함수를 만들었지 만 빠르고 더러워 야 할 경우이를 수행합니다.


0

율리우스 력 날짜 0이 월요일이기 때문에 일요일에 -1 전날 인 주 수를 추가하십시오. select dateadd (wk, datediff (wk, 0, getdate ()),-1)


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를 사용하여 해당주의 이전 월요일에 있었던 날짜를 계산합니다.


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

1
나는 이것이 나를 위해 작동하지 않는다는 것을 알았습니다. My @@DATEFIRST도 7이지만 귀하 @testDate가주의 시작일 경우 전날 날짜를 반환합니다.
row1

-1

비슷한 문제가있었습니다. 날짜가 주어지면 그주의 월요일 날짜를 알고 싶었습니다.

다음 논리를 사용했습니다. 0-6 범위의 주에서 일 수를 찾은 다음 시작 날짜에서 빼십시오.

나는 사용했다 : DATEADD (day,-(DATEPART (weekday,) + 5) % 7,)

DATEPRRT (weekday,)는 1 = Sundaye ... 7 = Saturday를 반환하므로 DATEPART (weekday,) + 5) % 7은 0 = Monday ... 6 = Sunday를 반환합니다.

원래 날짜에서이 일 수를 빼면 이전 월요일이됩니다. 동일한 기술을 모든 요일에 사용할 수 있습니다.


-1

나는 이것이 간단하고 유용하다는 것을 알았다. 요일이 일요일 또는 월요일 인 경우에도 작동합니다.

@BaseDate AS 날짜 선언

SET @BaseDate = GETDATE ()

@FisrtDOW를 날짜로 선언

SELECT @FirstDOW = DATEADD (d, DATEPART (WEEKDAY, @ BaseDate) * -1 + 1, @BaseDate)


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

에 대해 다른 설정을 시도하면 여기에서 다른 답변을 얻을 수 있습니다 SET DATEFIRST.
Aaron Bertrand

5
글쎄, 나는 투표하지 않았지만 당신의 대답은 DATEFIRST(지금 3 년 반 동안) 전혀 언급 하지 않았고 여전히 그렇지 않습니다. 그리고 당신은해야한다 또한 같은 지역 포맷을 피하기 m/d/y도 m d는 동일 시나리오에서.
Aaron Bertrand
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.