Yanıtlar:
Verilen:
Ayın 3. Cuma günü her zaman ayın 15-21'inden itibaren
select thedate
from yourtable
where datename(weekday, thedate) = 'Friday'
and datepart(day, thedate)>=15 and datepart(day, thedate)<=21;
Ayrıca kullanabilirsiniz weekday
ile datepart()
, ancak IMO bir isimle daha okunabilir bu. Dize karşılaştırmaları elbette daha yavaş olacaktır.
Dilden / kültürden bağımsız bir cevap almak için farklı hafta içi isimleri ve haftanın başlangıcını hesaba katmanız gerekir.
İtalya'da Cuma "Venerdì" ve haftanın ilk günü ABD'de olduğu gibi Pazar değil Pazartesi.
1900-01-01
Pazartesi günüydü, bu yüzden bu bilgiyi hafta içi yerelden bağımsız bir şekilde hesaplamak için kullanabiliriz:
WITH dates AS (
SELECT DATEADD(day, number, GETDATE()) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT theDate, DATENAME(dw, theDate), DATEPART(dw, theDate)
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 4
AND DATEPART(day, thedate)>=15 and DATEPART(day, thedate)<=21;
Başka bir yol, Phil'in cevabını temel olarak kullanır ve farklı ortamlarla ilgilenir:
select thedate
from yourtable
where (datepart(weekday, thedate) + @@DATEFIRST - 2) % 7 + 1 = 5 -- 5 -> Friday
and (datepart(day, thedate) - 1) / 7 + 1 = 3 ; -- 3 -> 3rd week
5
Kodu (Cuma dışındaki bir hafta içi istiyorsanız) (aynı olmalıdır SET DATEFIRST
kodlar):
1 for Monday
2 for Tuesday
3 for Wednesday
4 for Thursday
5 for Friday
6 for Saturday
7 for Sunday
Dil ayarları karşısında güvende olmak için "bilinen iyi" bir tarihi de kullanabilirsiniz. Örneğin, Cuma günleri arıyorsanız, bir takvimi kontrol edin ve 2 Ocak 2015'in bir Cuma olduğunu görün. İlk karşılaştırma daha sonra şu şekilde yazılabilir:
DATEPART(weekday,thedate) = DATEPART(weekday,'20150102') --Any Friday
Ayrıca bkz . Peter Larsson tarafından bir ayın nci haftası nasıl alınır?
Aslında burada bu tür bir hesaplama hakkında bir makale yazdım
Temel olarak, herhangi bir tarih aralığında her ayın 3. Cuma gününü bulmak için aşağıdaki kodu kullanabilirsiniz
USE TEMPDB
set nocount on;
IF OBJECT_ID('dbo.#t') is not null
DROP TABLE dbo.#t;
CREATE TABLE #t ([Date] datetime,
[Year] smallint, [Quarter] tinyint, [Month] tinyint
, [Day] smallint -- from 1 to 366 = 1st to 366th day in a year
, [Week] tinyint -- from 1 to 54 = the 1st to 54th week in a year;
, [Monthly_week] tinyint -- 1/2/3/4/5=1st/2nd/3rd/4th/5th week in a month
, [Week_day] tinyint -- 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat, 7=Sun
);
GO
USE TEMPDB
-- populate the table #t, and the day of week is defined as
-- 1=Mon, 2=Tue, 3=Wed, 4=Thu,5=Fri, 6=Sat, 7=Sun
;WITH C0 AS (SELECT c FROM (VALUES(1),(1)) AS D(c)),
C1 AS (SELECT 1 AS c FROM C0 AS A CROSS JOIN C0 AS B),
C2 AS (SELECT 1 AS c FROM C1 AS A CROSS JOIN C1 AS B),
C3 AS (SELECT 1 AS c FROM C2 AS A CROSS JOIN C2 AS B),
C4 AS (SELECT 1 AS c FROM C3 AS A CROSS JOIN C3 AS B),
C5 AS (SELECT 1 AS c FROM C4 AS A CROSS JOIN C3 AS B),
C6 AS (select rn=row_number() over (order by c) from C5),
C7 as (select [date]=dateadd(day, rn-1, '19000101') FROM C6 WHERE rn <= datediff(day, '19000101', '99991231')+1)
INSERT INTO #t ([year], [quarter], [month], [week], [day], [monthly_week], [week_day], [date])
SELECT datepart(yy, [DATE]), datepart(qq, [date]), datepart(mm, [date]), datepart(wk, [date])
, datediff(day, dateadd(year, datediff(year, 0, [date]), 0), [date])+1
, datepart(week, [date]) -datepart(week, dateadd(month, datediff(month, 0, [date]) , 0))+1
, CASE WHEN datepart(dw, [date])+@@datefirst-1 > 7 THEN (datepart(dw, [date])+@@datefirst-1)%7
ELSE datepart(dw, [date])+@@datefirst-1 END
, [date]
FROM C7
--where [date] between '19900101' and '20990101'; -- if you want to populate a range of dates
GO
select convert(char(10), [Date], 120)
from #t
where Monthly_week=3
and week_day=5
and [date] between '2015-01-01' and '2015-12-31' -- change to your own date range
Evet, bunun eski bir gönderi olduğunu biliyorum. Yaşına rağmen olaylara farklı bir eğim sağlayacağımı düşündüm. Heh ... ve özür dilerim. Ben sadece @jyao yukarıda ne gönderdiğini neredeyse çoğaltmak olduğunu fark ettim.
OP'nin orijinal sorusunun şu anki düzenlemesine dayanarak, insanların neden cevaplarını nerede gönderdiklerini anlayamadım.
Düzenlemeleri inceleyerek orijinal soruyu buldum ve aşağıya gönderdim ...
Bir SQL veritabanında 1.1.1996-30.8.2014 arasında değişen bir zaman serisi var, örneğin tablo "db.dbo.datestable" ile.
SQL bu tarih aralığı için "her ayın 3. Cuma" olan tarihleri belirlemek gerekir.
Ben "rank = 3" ayarlamak için "DENSE_RANK ()" ve "PARTITION BY ()" bir arada kullanmanız gerektiğini umuyorum. Ancak, SQL için yeniyim ve doğru kodu bulamıyorum.
Bu sorunu çözebilir misin?
Orijinal sorunun cesurca vurgulayarak vurguladığım kısmı anahtar gibi görünüyor. Kesinlikle yanlış olabilirim ama bana göre OP, "dbo.datestable" adlı bir "Takvim" tablosu olduğunu ve bana göre bu büyük bir fark yaratıyor ve şimdi neden cevapların çoğunun "10bo.datestable" referansının son kalıntılarını kaldıran, 10 Kasım'da ... sorudaki son düzenlemeden bir gün sonra yayınlandığı için tarihleri oluşturanları da içerirler.
Dediğim gibi, yanılmış olabilirim ama işte orijinal soruyu yorumum.
"Dbo.datestable" adlı bir "Takvim" tablo var. Bu tablonun kapsadığı herhangi bir tarih aralığı göz önüne alındığında, belirli bir tarih aralığında yalnızca her ayın 3. Cuma günlerini nasıl iade edebilirim?
Bunu yapmak için geleneksel yöntemler zaten ele alındığı için, bazıları için yararlı olabilecek bir alternatif ekleyeceğim.
OP'nin masasında zaten olacağını düşündüğüm birkaç sütunu simüle edelim. Tabii ki, sütun adlarını tahmin ediyorum. Lütfen "Takvim" tablonuz için eşdeğer sütunları ne olursa olsun alt bölümlere ayırın. Ayrıca, tüm bunları TempDB'de yapıyorum, bu yüzden birisinin gerçek "Takvim" tablosuna müdahale etme şansım yok.
--=================================================================================================
-- Simulate just a couple of the necessary columns of the OPs "Calendar" table.
-- This is not a part of the solution. We're just trying to simulate what the OP has.
--=================================================================================================
--===== Variables to control the dates that will appear in the "Calendar" table.
DECLARE @StartDT DATETIME
,@EndDT DATETIME
;
SELECT @StartDT = '1900' --Will be inclusive start of this year in calculations.
,@EndDT = '2100' --Will be exclusive start of this year in calculations.
;
--===== Create the "Calendar" table with just enough columns to simulate the OP's.
CREATE TABLE #datestable
(
TheDate DATETIME NOT NULL
,DW TINYINT NOT NULL --SQL standard abbreviate of "Day of Week"
)
;
--===== Populate the "Calendar" table (uses "Minimal Logging" in 2008+ this case).
WITH cteGenDates AS
(
SELECT TOP (DATEDIFF(dd,@StartDT,@EndDT)) --You can use "DAY" instead of "dd" if you prefer. I don't like it, though.
TheDate = DATEADD(dd, ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1, @StartDT)
FROM sys.all_columns ac1
CROSS JOIN sys.all_columns ac2
)
INSERT INTO #datestable WITH (TABLOCK)
SELECT TheDate
,DW = DATEDIFF(dd,0,TheDate)%7+1 --Monday is 1, Friday is 5, Sunday is 7 etc.
FROM cteGenDates
OPTION (RECOMPILE) -- Help keep "Minimal Logging" in the presence of variables.
;
--===== Add the expected named PK for this example.
ALTER TABLE #datestable
ADD CONSTRAINT PK_datestable PRIMARY KEY CLUSTERED (TheDate)
;
Ayrıca OP'nin "Takvim" tablosunda değişiklik yapıp yapamayacağını bilmediğim için bu ona yardımcı olmayabilir, ancak başkalarına yardımcı olabilir. Bunu göz önünde bulundurarak, bir DWoM (Ay için Haftanın Günü) sütunu ekleyelim. Adı beğenmediyseniz, lütfen kendi kutunuzda ihtiyacınız olan şeyle değiştirmekten çekinmeyin.
--===== Add the new column.
ALTER TABLE #datestable
ADD DWOM TINYINT NOT NULL DEFAULT (0)
;
Sonra, yeni sütunu doldurmamız gerekiyor. OP'nin orijinal katkısız görevinde bunu anladı.
--===== Populate the new column using the CTE trick for updates so that
-- we can use a Windowing Function in an UPDATE.
WITH cteGenDWOM AS
(
SELECT DW# = ROW_NUMBER() OVER (PARTITION BY DATEDIFF(mm,0,TheDate), DW
ORDER BY TheDate)
,DWOM
FROM #datestable
)
UPDATE cteGenDWOM
SET DWOM = DW#
;
Şimdi, bu sabit uzunluklu bir sütun olduğu için, sadece bir grup sayfa bölünmesi yarattı, bu nedenle kümelenmiş Dizini, performans açısından sayfa başına mümkün olduğunca çok satıra sahip olacak şekilde tabloyu "yeniden paketlemek" için yeniden oluşturmamız gerekiyor.
--===== "Repack" the Clustered Index to get rid of the page splits we
-- caused by adding the new column.
ALTER INDEX PK_datestable
ON #datestable
REBUILD WITH (FILLFACTOR = 100, SORT_IN_TEMPDB = ON)
;
Bu yapıldıktan sonra, belirli bir tarih aralığında her ayın 3. Cuma'sını döndürmek gibi şeyler yapan sorgular önemsiz ve okunması oldukça açık hale gelir.
--===== Return the 3rd Friday of every month included in the given date range.
SELECT *
FROM #datestable
WHERE TheDate >= '1996-01-01' --I never use "BETWEEN" for dates out of habit for end date offsets.
AND TheDate <= '2014-08-30'
AND DW = 5 --Friday
AND DWOM = 3 --The 3rd one for every month
ORDER BY TheDate
;
İşte basit bir kes ve yapıştır çözümü. İsterseniz bunu bir işleve dönüştürebilirsiniz.
Declare @CurrDate Date
Set @CurrDate = '11-20-2016'
declare @first datetime -- First of the month of interest (no time part)
declare @nth tinyint -- Which of them - 1st, 2nd, etc.
declare @dow tinyint -- Day of week we want
set @first = DATEFROMPARTS(YEAR(@CurrDate), MONTH(@CurrDate), 1)
set @nth = 3
set @dow = 6
declare @result datetime
set @result = @first + 7*(@nth-1)
select @result + (7 + @dow - datepart(weekday,@result))%7