Próbuję grupować rekordy według tygodnia, zapisując zagregowaną datę jako pierwszy dzień tygodnia. Jednak standardowa technika, której używam do zaokrąglania dat, nie wydaje się działać poprawnie z tygodniami (chociaż działa w przypadku dni, miesięcy, lat, kwartałów i innych ram czasowych, do których ją zastosowałem).
Oto SQL:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);
To powraca 2011-08-22 00:00:00.000
, czyli w poniedziałek, a nie w niedzielę. Wybranie @@datefirst
zwrotów 7
, czyli kodu na niedzielę, dzięki czemu serwer jest skonfigurowany poprawnie, o ile wiem.
Mogę to łatwo ominąć, zmieniając powyższy kod na:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);
Ale fakt, że muszę zrobić taki wyjątek, trochę mnie niepokoi. Przepraszamy, jeśli jest to powtórzone pytanie. Znalazłem kilka powiązanych pytań, ale żadne nie dotyczyło konkretnie tego aspektu.
źródło
(@@DATEFIRST + DATEPART(DW, @SomeDate)) % 7
@@datefirst
myślę, że pozostaje niezmienna niezależnie od ustawienia. Poniedziałek = 2.Odpowiedzi:
Aby odpowiedzieć, dlaczego otrzymujesz poniedziałek, a nie niedzielę:
Dodajesz liczbę tygodni do daty 0. Co to jest data 0? 1900-01-01. Jaki był dzień 01.01.1900? Poniedziałek. Więc w swoim kodzie mówisz, ile tygodni minęło od poniedziałku 1 stycznia 1900 roku? Nazwijmy to [n]. Ok, teraz dodaj [n] tygodni do poniedziałku 1 stycznia 1900. Nie powinieneś być zaskoczony, że kończy się to w poniedziałek.
DATEADD
nie ma pojęcia, że chcesz dodawać tygodnie, ale tylko do niedzieli, po prostu dodaje 7 dni, a następnie dodaje 7 kolejnych dni ... tak jakDATEDIFF
rozpoznaje tylko granice, które zostały przekroczone. Na przykład oba zwracają 1, mimo że niektórzy ludzie narzekają, że powinna być wbudowana rozsądna logika do zaokrąglania w górę lub w dół:SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31'); SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');
Aby odpowiedzieć, jak dostać niedzielę:
Jeśli chcesz niedzielę, wybierz datę bazową, która nie jest poniedziałek, ale raczej niedziela. Na przykład:
DECLARE @dt DATE = '1905-01-01'; SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);
To się nie zepsuje, jeśli zmienisz swoje
DATEFIRST
ustawienie (lub Twój kod jest uruchomiony dla użytkownika z innym ustawieniem) - pod warunkiem, że nadal chcesz niedzielę, niezależnie od bieżącego ustawienia. Jeśli chcesz, aby te dwie odpowiedzi były jive, powinieneś użyć funkcji, która zależy odDATEFIRST
ustawienia, npSELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
Więc jeśli zmienisz
DATEFIRST
ustawienie na poniedziałek, wtorek, co masz, zachowanie się zmieni. W zależności od tego, jakiego zachowania chcesz, możesz użyć jednej z następujących funkcji:CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday ( @d DATE ) RETURNS DATE AS BEGIN RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @d), '19050101')); END GO
...lub...
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
Masz teraz wiele alternatyw, ale która z nich działa najlepiej? Zdziwiłbym się, gdyby były jakieś większe różnice, ale zebrałem wszystkie dotychczas udzielone odpowiedzi i przeprowadziłem je przez dwa zestawy testów - jeden tani i jeden drogi. Zmierzyłem statystyki klienta, ponieważ nie widzę tutaj I / O lub pamięci odgrywających rolę w wydajności (chociaż mogą one mieć znaczenie w zależności od sposobu wykorzystania funkcji). W moich testach wyniki to:
Zapytanie o przypisanie „Tanie”:
Zapytanie o przypisanie „Drogie”:
W razie potrzeby mogę przekazać szczegóły moich testów - zatrzymując się tutaj, ponieważ jest to już dość rozwlekłe. Byłem trochę zaskoczony, widząc, że Curt wyszedł jako najszybszy na najwyższym poziomie, biorąc pod uwagę liczbę obliczeń i kod wbudowany. Może przeprowadzę dokładniejsze testy i napiszę o tym na blogu ... jeśli nie macie żadnych zastrzeżeń co do tego, żebym opublikował wasze funkcje gdzie indziej.
źródło
Dla tych, którzy potrzebują:
Poniedziałek = 1 i Niedziela = 7:
SELECT 1 + ((5 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
Niedziela = 1 i Sobota = 7:
SELECT 1 + ((6 + DATEPART(dw, GETDATE()) + @@DATEFIRST) % 7);
Powyżej był podobny przykład, ale dzięki podwójnemu „% 7” byłby znacznie wolniejszy.
źródło
select (datediff(dd,5,cal.D_DATE)%7 + 1)
iselect (datediff(dd,6,cal.D_DATE)%7 + 1)
Dla tych, którzy potrzebują odpowiedzi w pracy, a tworzenie funkcji jest zabronione przez Twojego administratora, sprawdzi się następujące rozwiązanie:
select *, cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-1), YourDate) as DATE) as WeekStart From.....
To daje początek tego tygodnia. Tutaj zakładam, że niedziele to początek tygodni. Jeśli myślisz, że poniedziałek to początek, zastosuj:
select *, cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-2), YourDate) as DATE) as WeekStart From.....
źródło
To działa wspaniale dla mnie:
źródło
Wygooglowałem ten skrypt:
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
źródło
Może potrzebujesz tego:
SELECT DATEADD(DD, 1 - DATEPART(DW, GETDATE()), GETDATE())
Lub
DECLARE @MYDATE DATETIME SET @MYDATE = '2011-08-23' SELECT DATEADD(DD, 1 - DATEPART(DW, @MYDATE), @MYDATE)
Funkcjonować
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
źródło
DATEPART(DW
zależy od@@datefirst
DATE
a wtedy nie trzeba wykonywać żadnych nieoptymalnych konwersji doVARCHAR
i z powrotem, tylko po to, aby usunąć wszelkie przekazane przypadkowe składniki czasu.Time
wartości.źródło
Podstawowy (niedziela bieżącego tygodnia)
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) as date)
Jeśli poprzedni tydzień:
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) -7 as date)
Wewnętrznie zbudowaliśmy funkcję, która to robi, ale jeśli potrzebujesz szybkiego i brudnego, zrobi to.
źródło
Ponieważ data juliańska 0 to poniedziałek, wystarczy dodać liczbę tygodni do niedzieli, czyli dzień przed -1 Np. select dateadd (wk, datediff (wk, 0, getdate ()), - 1)
źródło
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
Taka jest moja logika. Ustaw pierwszy dzień tygodnia na poniedziałek, a następnie oblicz, jaki jest dzień tygodnia danego dnia, a następnie używając DateAdd i Case obliczam, jaka data byłaby w poprzedni poniedziałek tego tygodnia.
źródło
Nie mam żadnych problemów z żadną z podanych tutaj odpowiedzi, jednak myślę, że moja jest o wiele prostsza do wdrożenia i zrozumienia. Nie przeprowadzałem na nim żadnych testów wydajności, ale powinno to być pomijalne.
Tak więc swoją odpowiedź zaczerpnąłem z faktu, że daty są przechowywane na serwerze SQL jako liczby całkowite (mówię tylko o komponencie daty). Jeśli mi nie wierzysz, wypróbuj to SELECT CONVERT (INT, GETDATE ()) i na odwrót.
Teraz wiedząc o tym, możesz zrobić fajne równania matematyczne. Możesz wymyślić lepszy, ale tutaj jest mój.
/* 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))
źródło
@@DATEFIRST
to również 7, ale jeśli@testDate
jest to początek tygodnia, zwraca datę, która jest dzień wcześniej.Miałem podobny problem. Biorąc pod uwagę datę, chciałem poznać poniedziałek tego tygodnia.
Użyłem następującej logiki: znajdź numer dnia tygodnia z zakresu 0-6, a następnie odejmij go od daty początkowej.
Użyłem: DATEADD (dzień, - (DATEPART (dzień tygodnia,) + 5)% 7,)
Ponieważ DATEPRRT (dzień tygodnia,) zwraca 1 = niedzielae ... 7 = sobota, DATEPART (dzień roboczy,) + 5)% 7 zwraca 0 = poniedziałek ... 6 = niedziela.
Odejmowanie tej liczby dni od pierwotnej daty daje poprzedni poniedziałek. Tę samą technikę można zastosować w każdym początkowym dniu tygodnia.
źródło
Wydało mi się to proste i przydatne. Działa nawet jeśli pierwszy dzień tygodnia to niedziela lub poniedziałek.
ZADEKLAROWAĆ @BaseDate jako datę
SET @BaseDate = GETDATE ()
ZADEKLAROWAĆ @FisrtDOW jako datę
SELECT @FirstDOW = DATEADD (d, DATEPART (WEEKDAY, @ BaseDate) * -1 + 1, @BaseDate)
źródło
Może nadmiernie upraszczam tutaj i może tak być, ale wydaje mi się, że to działa. Nie napotkałem jeszcze żadnych problemów ...
źródło
SET DATEFIRST
.DATEFIRST
(od trzech i pół roku) i nadal nie ma. Powinieneś także unikać formatów regionalnych, takich jakm/d/y
, nawet w scenariuszach, w których m i d są takie same.