Ja, ich weiß, dass dies ein alter Beitrag ist. Ich dachte, ich würde den Dingen trotz ihres Alters eine andere Richtung geben. Heh ... und ich entschuldige mich. Mir ist gerade aufgefallen, dass ich das, was @jyao oben gepostet hat, fast dupliziert habe.
Anhand der aktuellen Ausgabe der ursprünglichen Frage des OP konnte ich nicht herausfinden, warum Personen die Antworten, die sie gaben, veröffentlicht haben.
Bei der Durchsicht der Änderungen habe ich die ursprüngliche Frage gefunden und sie unten gepostet ...
Ich habe eine Zeitreihe vom 1.1.1996 - 30.8.2014 in einer SQL-Datenbank, zB mit der Tabelle "db.dbo.datestable".
Ich muss die Daten bestimmen, die der "3. Freitag jedes Monats" für diesen Datumsbereich in SQL sind.
Ich gehe davon aus, dass ich eine Kombination aus "DENSE_RANK ()" und "PARTITION BY ()" verwenden sollte, um "rank = 3" festzulegen. Ich bin jedoch neu in SQL und kann den richtigen Code nicht finden.
Können Sie dieses Problem lösen?
Der Teil der ursprünglichen Frage, den ich durch Fettdruck hervorgehoben habe, scheint der Schlüssel zu sein. Ich könnte mich sicherlich irren, aber mir scheint, dass das OP angab, dass er eine "Kalender" -Tabelle mit dem Namen "dbo.datestable" hat, und das macht für mich einen enormen Unterschied und ich verstehe jetzt, warum viele der Antworten sind, was sie sind, einschließlich derjenigen, die die Daten generiert, weil es am 10. November veröffentlicht wurde ... einen Tag nach der endgültigen Bearbeitung der Frage, die die letzten Überreste des Verweises auf die "dbo.datestable" entfernt.
Wie gesagt, ich könnte mich irren, aber hier ist meine Interpretation der ursprünglichen Frage.
Ich habe eine "Kalender" -Tabelle mit dem Namen "dbo.datestable". Wie kann ich bei einem bestimmten Datumsbereich, der von dieser Tabelle abgedeckt wird, nur die Daten zurückgeben, die der 3. Freitag eines jeden Monats innerhalb des angegebenen Datumsbereichs sind?
Da die herkömmlichen Methoden hierfür bereits behandelt wurden, füge ich eine Alternative hinzu, die für einige hilfreich sein könnte.
Lassen Sie uns ein paar der Spalten simulieren, von denen ich denke, dass sie das OP bereits in seiner Tabelle haben wird. Natürlich schätze ich die Spaltennamen. Bitte tragen Sie die entsprechenden Spalten für Ihre "Kalender" -Tabelle ein. Außerdem mache ich das alles in TempDB, damit ich nicht die Chance nutze, die echte "Kalender" -Tabelle von jemandem zu stören.
--=================================================================================================
-- 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)
;
Es ist auch eine Selbstverständlichkeit, dass ich nicht weiß, ob das OP Änderungen an seiner "Kalender" -Tabelle vornehmen kann, so dass dies ihm möglicherweise nicht hilft, aber es kann anderen helfen. In diesem Sinne fügen wir eine DWoM-Spalte (Wochentag für den Monat) hinzu. Wenn Ihnen der Name nicht gefällt, können Sie ihn jederzeit auf Ihrer eigenen Box ändern.
--===== Add the new column.
ALTER TABLE #datestable
ADD DWOM TINYINT NOT NULL DEFAULT (0)
;
Als nächstes müssen wir die neue Spalte füllen. Der OP hatte ein Gefühl dafür in seinem ursprünglichen unverfälschten Posten.
--===== 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#
;
Da es sich um eine Spalte mit fester Länge handelt, wurden nur einige Seitenteile erstellt, sodass der Clustered Index neu erstellt werden muss, damit die Tabelle aus Gründen der Leistung so viele Zeilen wie möglich enthält.
--===== "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)
;
Sobald dies erledigt ist, werden Abfragen, bei denen beispielsweise der 3. Freitag eines jeden Monats in einem bestimmten Datumsbereich zurückgegeben wird, trivial und ziemlich offensichtlich zu lesen.
--===== 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
;