SQL Server'da haftanın ilk gününü alın


97

Kayıtları haftaya göre gruplandırmaya çalışıyorum, toplanan tarihi haftanın ilk günü olarak saklamaya çalışıyorum. Bununla birlikte, tarihleri ​​yuvarlamak için kullandığım standart teknik haftalarla doğru çalışmıyor gibi görünüyor (günler, aylar, yıllar, çeyrekler ve uyguladığım diğer zaman dilimleri için olsa da).

İşte SQL:

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

Bu 2011-08-22 00:00:00.000, Pazar değil, Pazartesi olan geri döner . Pazar için kod olan @@datefirstdönüşleri seçmek 7, böylece sunucu bildiğim kadarıyla doğru bir şekilde kuruluyor.

Yukarıdaki kodu şu şekilde değiştirerek bunu kolayca atlayabilirim:

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

Ama böyle bir istisna yapmam gerektiği gerçeği beni biraz tedirgin ediyor. Ayrıca, bu yinelenen bir soruysa özür dileriz. İlgili bazı sorular buldum, ancak hiçbiri bu yönü özellikle ele almadı.


9
(@@DATEFIRST + DATEPART(DW, @SomeDate)) % 7@@datefirstbence ayar ne olursa olsun sabit kalır . Pazartesi ile = 2.
Martin Smith

Yanıtlar:


150

Neden Pazar günü değil de Pazartesi olduğunu yanıtlamak için:

0 tarihine birkaç hafta ekliyorsunuz. Tarih 0 nedir? 1900-01-01. 1900-01-01'de gün neydi? Pazartesi. Yani kodunuzda diyorsunuz ki, 1 Ocak 1900 Pazartesi'den bu yana kaç hafta geçti? Buna [n] diyelim. Tamam, şimdi 1 Ocak 1900 Pazartesi gününe [n] hafta ekleyin. Bunun Pazartesi olmasına şaşırmamalısınız. DATEADDhafta eklemek istediğinizi bilmiyor ama sadece Pazar gününe gelene kadar, sadece 7 gün ekliyor, ardından 7 gün daha ekliyor ... tıpkı DATEDIFFsadece aşılan sınırları tanıması gibi . Örneğin, bazıları yukarı veya aşağı yuvarlamak için yerleşik mantıklı bir mantık olması gerektiğinden şikayet etse de, bunların ikisi de 1 döndürür:

SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31');
SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');

Pazar gününün nasıl alınacağını cevaplamak için:

Pazar günü istiyorsanız, Pazartesi değil Pazar olan bir temel tarih seçin. Örneğin:

DECLARE @dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);

DATEFIRSTGeçerli ayardan bağımsız olarak Pazar günü olmasını istiyorsanız , ayarınızı değiştirirseniz (veya kodunuz farklı bir ayara sahip bir kullanıcı için çalışıyorsa) bu işlem bozulmayacaktır . Eğer jive için bu iki cevap istiyorsanız, o zaman bir işlevi kullanmalısınız gelmez bağlıdır DATEFIRSTayarı, örneğin

SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);

Yani, DATEFIRSTayarınızı Pazartesi, Salı olarak değiştirirseniz, elinizde ne var, davranış değişecektir. Hangi davranışı istediğinize bağlı olarak şu işlevlerden birini kullanabilirsiniz:

CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday
(
    @d DATE
)
RETURNS DATE
AS
BEGIN
    RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @d), '19050101'));
END
GO

...veya...

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

Şimdi, birçok alternatifiniz var, ancak hangisi en iyi performansı gösteriyor? Herhangi bir büyük fark olursa şaşırırdım ama şimdiye kadar verilen tüm cevapları topladım ve bunları biri ucuz ve biri pahalı olmak üzere iki test setinden geçirdim. İstemci istatistiklerini ölçtüm çünkü burada G / Ç veya belleğin performansta bir rol oynadığını görmüyorum (ancak bunlar işlevin nasıl kullanıldığına bağlı olarak devreye girebilir). Testlerimde sonuçlar:

"Ucuz" atama sorgusu:

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

"Pahalı" atama sorgusu:

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

İsterseniz testlerimin ayrıntılarını aktarabilirim - burada durarak bu zaten oldukça uzun sürüyor. Hesaplamaların sayısı ve satır içi kod göz önüne alındığında, Curt'ün en yüksek seviyede en hızlı çıktığını görmek beni biraz şaşırttı. Belki biraz daha kapsamlı testler yapacağım ve bununla ilgili blog yazacağım ... eğer fonksiyonlarınızı başka bir yerde yayınlamama itirazınız yoksa.


Dolayısıyla, haftalarımın Pazar günü başlayıp Cumartesi günü sona erdiğini düşünürsem, herhangi bir @d tarihi için haftanın son gününü elde edebilirim : SELECT DATEADD (wk, DATEDIFF (wk, '19041231', @d), '19041231')
Baodad

22

Alması gerekenler için:

Pazartesi = 1 ve Pazar = 7:

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

Pazar = 1 ve Cumartesi = 7:

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

Yukarıda benzer bir örnek vardı, ancak "% 7" nin ikiye katlanması çok daha yavaş olacaktır.


1
Bu, haftanın başından itibaren Paz veya Pzt olan gün numarasını almak için de harika çalışıyor. Teşekkürler
Fandango68

Alternatif olarak select (datediff(dd,5,cal.D_DATE)%7 + 1)veselect (datediff(dd,6,cal.D_DATE)%7 + 1)
vasja

9

İşte cevaba ihtiyaç duyanlar ve işlev oluşturmak DBA'nız tarafından yasaklanmışsa, aşağıdaki çözüm işe yarayacaktır:

select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-1), YourDate) as DATE) as WeekStart
From.....

Bu, o haftanın başlangıcını verir. Burada pazar günlerinin haftaların başlangıcı olduğunu varsayıyorum. Pazartesi gününün başlangıç ​​olduğunu düşünüyorsanız, şunu kullanmalısınız:

select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-2), YourDate) as DATE) as WeekStart
From.....

5

Bu benim için harika çalışıyor:

FONKSİYON OLUŞTURMA [dbo]. [Haftanın Başlangıcı]
(
  @ INPUTDATE DATETIME
)
İADE TARİHİ

GİBİ
BAŞLA
  - BU işlevde çalışmıyor.
  - TARİHİ AYARLA 1 - pazartesiyi haftanın ilk günü olarak ayarlayın.

  DECLARE @DOW INT - haftanın gününü saklamak için
  SET @ INPUTDATE = CONVERT (VARCHAR (10), @INPUTDATE, 111)
  SET @DOW = DATEPART (DW, @INPUTDATE)

  - Pazartesinin sihirli dönüşümü 1, salı 2, vb.
  - SQL sunucusunun hafta başı hakkında ne düşündüğüne aldırış etmeyin.
  - Ama burada pazar gününü 0 olarak işaretledik, ama bunu daha sonra düzeltiriz.
  SET @DOW = (@DOW + @@ DATEFIRST - 1)% 7
  EĞER @DOW = 0 SET @DOW = 7 - pazar için düzeltme

  İADE TARİHİ ADD (GG, 1 - @ DOW, @ INPUTDATE)

SON

Bu, Pazar günü değil, bugünün tarihine verilen Pazartesi'ye dönüyor gibi görünüyor. OP'nin zaten Pazartesi'yi döndüren bir işlevi var, Pazar günü dönmesini istiyor. :-)
Aaron Bertrand

doh! Bir dahaki sefere soruları daha dikkatli okumalıyım. Bununla birlikte, yine de gerekirse çözümüm kolayca ayarlanabilir. Görünüşe göre OP kabul edilen cevaptan memnun görünüyor -)
trailmax

Bu, makinemde doğru çözüm çünkü benim için: DATEADD (ww, DATEDIFF (ww, 0, CONVERT (DATE, '2017-10-8')), 0) 2017-10-9 döndürüyor!
CMD

3

Bu komut dosyasını Google'da araştırdım:

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

Belki buna ihtiyacın var:

SELECT DATEADD(DD, 1 - DATEPART(DW, GETDATE()), GETDATE())

Veya

DECLARE @MYDATE DATETIME
SET @MYDATE = '2011-08-23'
SELECT DATEADD(DD, 1 - DATEPART(DW, @MYDATE), @MYDATE)

Fonksiyon

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(DWbağımlıdır@@datefirst
Martin Smith

Bunun sadeliğini beğendim. Çok büyük veri kümeleri için de oldukça iyi çalışıyor gibi görünüyor.
Quick Joe Smith

2
Neden sadece giriş parametresini DATEyapmıyorsunuz, o zaman VARCHARsadece içeri giren herhangi bir kaza sonucu zaman bileşenini ortadan kaldırmak için optimumun altında dönüşümler yapmanız gerekmiyor .
Aaron Bertrand

Döndürülen değerin Timedeğerlere ihtiyacı olmadığı için dönüştürme işlevi kullanılmıştır .
Gandarez

1
Evet ama asıl mesele şu ki varchar'a çevirip tekrar geri almak pahalı. Sadece bir TARİH parametreniz varsa, o zaman zamanın dahil edilip edilmediğini umursamıyorsunuz ... sizin için çıkarılır.
Aaron Bertrand

2
FONKSİYON OLUŞTURMA dbo.fnFirstWorkingDayOfTheWeek
(
    @currentDate tarih
)
İADE İNT
GİBİ
BAŞLA
    - DATEFIRST ayarını alın
    @Ds int = @@ DATEFIRST BİLDİRMEK 
    - mevcut DATEFIRST ayarı altında haftanın gün numarasını alın
    DECLARE @dow int = DATEPART (dw, @ currentDate) 

    DECLARE @wd int = 1 + (((@ dow + @ ds)% 7) +5)% 7 - bu her zaman Pzt'i 1, Sal'ı 2 ... Paz'ı 7 olarak döndürür 

    İADE TARİHİ ADD (gg, 1- @ wd, @ currentDate) 

SON

Bu benim için SQL Server 2005'te çalışan tek işlevdir. Teşekkürler
Fandango68

@ Fernando68 Diğer çözümlerin nasıl işe yaramadığını açıklayabilir misiniz ?
Aaron Bertrand

@AaronBertrand üzgünüm hatırlamıyorum, ama sanırım hızlı bir cevaba odaklanıyordum ve seninkini denedim ama nedense benim için işe yaramadı.
Fandango68

@ Fernando68 Bu çok yardımcı oldu. : - \
Aaron Bertrand

2

Temel için (mevcut haftanın Pazar günü)

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

Önceki hafta ise:

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

Dahili olarak, bunu yapan bir işlev geliştirdik, ancak hızlı ve kirli ihtiyacınız varsa, bu iş görür.


0

Jülyen tarihi 0 Pazartesi olduğundan, -1 Örn'den önceki gün olan Pazar'a hafta sayısını ekleyin. dateadd seçin (wk, dateiff (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

Bu benim mantığım. Haftanın ilkini Pazartesi olarak ayarlayın, ardından verilen günün haftanın gününü hesaplayın, ardından DateAdd ve Case'i kullanarak o haftanın önceki Pazartesi günü tarihin ne olacağını hesaplıyorum.


-1

Burada verilen cevapların hiçbiriyle ilgili sorunum yok, ancak bence benimkini uygulamak ve anlamak çok daha kolay. Üzerinde herhangi bir performans testi yapmadım, ancak önemsiz olmalı.

Bu yüzden cevabımı, tarihlerin SQL sunucusunda tamsayılar olarak saklandığı gerçeğinden türettim (sadece tarih bileşeninden bahsediyorum). Bana inanmıyorsanız, bu SELECT CONVERT (INT, GETDATE ()) ve tam tersini deneyin.

Şimdi bunu bilerek, bazı harika matematik denklemleri yapabilirsiniz. Daha iyisini bulabilirsin, ama işte benim.

/*
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
Bunun benim için çalışmadığını anladım. Benim @@DATEFIRSTde 7'dir, ancak sizin @testDatehaftanın başlangıcıysa, bu, önceki gün olan bir tarihi döndürür.
satır1

-1

Benzer bir problemim vardı. Bir tarih verildiğinde, o haftanın Pazartesi gününün tarihini almak istedim.

Şu mantığı kullandım: Haftanın gün numarasını 0-6 aralığında bulun, ardından bunu başlangıç ​​tarihinden çıkarın.

Kullandım: DATEADD (gün, - (DATEPART (hafta içi) + 5)% 7,)

DATEPRRT (hafta içi,) 1 = Sundaye ... 7 = Cumartesi döndürdüğünden, DATEPART (hafta içi) + 5)% 7, 0 = Pazartesi ... 6 = Pazar döndürür.

Bu gün sayısının orijinal tarihten çıkarılması, önceki Pazartesi gününü verir. Aynı teknik, haftanın herhangi bir başlangıç ​​günü için kullanılabilir.


-1

Bunu basit ve kullanışlı buldum. Haftanın ilk günü Pazar veya Pazartesi olsa bile çalışır.

@BaseDate'i Tarih olarak BİLDİR

SET @BaseDate = GETDATE ()

@FisrtDOW TARİHİ OLARAK BİLDİR

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


-3

Belki burada fazla basitleştiriyorum ve durum bu olabilir, ama bu benim için çalışıyor gibi görünüyor. Henüz herhangi bir sorunla karşılaşmadım ...

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'

İçin farklı ayarları denerseniz burada farklı yanıtlar alabilirsiniz SET DATEFIRST.
Aaron Bertrand

5
Ben olumsuz oy vermedim, ama cevabınızdan hiç bahsetmediniz DATEFIRST(şimdi üç buçuk yıldır) ve hala etmiyor. Ve gerektiğini de gibi bölgesel biçimlerini önlemek m/d/yhatta m ve d aynıdır senaryolarda,.
Aaron Bertrand
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.