Sí, sé que esta es una publicación anterior. Pensé que daría una inclinación diferente a las cosas a pesar de su edad. Je ... y mis disculpas. Me acabo de dar cuenta de que casi he duplicado lo que @jyao publicó anteriormente.
Basado en la edición actual de la pregunta original del OP, no pude entender por qué las personas publicaron las respuestas que hicieron.
Al revisar las ediciones, encontré la pregunta original y la publiqué a continuación ...
Tengo una serie temporal que va del 1.1.1996 al 30.8.2014 en una base de datos SQL, por ejemplo, con la tabla "db.dbo.datestable".
Necesito determinar las fechas que son el "3er viernes de cada mes" para este rango de fechas en SQL.
Espero usar una combinación de "DENSE_RANK ()" y "PARTITION BY ()" para establecer "rank = 3". Sin embargo, soy nuevo en SQL y no puedo encontrar el código correcto.
¿Puedes resolver este problema?
La parte de la pregunta original que he enfatizado en negrita parece ser la clave. Ciertamente podría ser incorrecto, pero me parece que el OP decía que tiene una tabla "Calendario" llamada "dbo.datestable" y, para mí, eso hace una gran diferencia y ahora entiendo por qué muchas de las respuestas son lo que están incluyendo el que generó las fechas porque fue publicado el 10 de noviembre ... un día después de la edición final de la pregunta, que eliminó los vestigios finales de la referencia al "dbo.datestable".
Como dije, podría estar equivocado, pero aquí está mi interpretación de la pregunta original.
Tengo una tabla de "Calendario" llamada "dbo.datestable". Dado cualquier rango de fechas cubierto por esa tabla, ¿cómo puedo devolver solo las fechas que son el tercer viernes de cada mes dentro de ese rango de fechas?
Como los métodos convencionales para hacer esto ya han sido cubiertos, agregaré una alternativa que podría ser útil para algunos.
Simulemos un par de columnas que creo que el OP ya tendrá en su tabla. Por supuesto, estoy adivinando los nombres de las columnas. Subtique las columnas equivalentes para su tabla "Calendario". Además, estoy haciendo todo esto en TempDB para no tener la posibilidad de interferir con la tabla "Calendario" real de alguien.
--=================================================================================================
-- 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)
;
También es un hecho que no sé si el OP puede hacer cambios en su tabla "Calendario", por lo que esto podría no ayudarlo, pero puede ayudar a otros. Con eso en mente, agreguemos una columna DWoM (Día de la semana para el mes). Si no te gusta el nombre, no dude en cambiarlo a lo que usted necesita en su propia caja.
--===== Add the new column.
ALTER TABLE #datestable
ADD DWOM TINYINT NOT NULL DEFAULT (0)
;
A continuación, debemos completar la nueva columna. El OP tuvo una idea de esto en su publicación original no adulterada.
--===== 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#
;
Ahora, debido a que es una columna de longitud fija, que acaba de crear un montón de divisiones de página, por lo que debemos reconstruir el Índice agrupado para "volver a empaquetar" la tabla para tener tantas filas por página como sea posible en aras del rendimiento.
--===== "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)
;
Una vez hecho esto, las consultas que hacen cosas como regresar el tercer viernes de cada mes en un rango de fechas dado se vuelven triviales y bastante obvias de leer.
--===== 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
;