Да, я знаю, что это старый пост. Думал, я бы по-другому взглянул на вещи, несмотря на ее возраст. Хех ... и мои извинения. Я только что понял, что почти скопировал то, что @jyao опубликовал выше.
Основываясь на текущем редактировании исходного вопроса ОП, я не мог понять, почему люди, публикующие ответы, делали это.
Просматривая правки, я нашел оригинальный вопрос и разместил его ниже ...
У меня есть временной ряд от 1.1.1996 до 30.8.2014 в базе данных SQL, например, с таблицей "db.dbo.datestable".
Мне нужно определить даты, которые являются «3-й пятницей каждого месяца» для этого диапазона дат в SQL.
Я ожидаю, что я должен использовать комбинацию "DENSE_RANK ()" и "PARTITION BY ()", чтобы установить "rank = 3". Тем не менее, я новичок в SQL и не могу найти правильный код.
Вы можете решить эту проблему?
Часть первоначального вопроса, которую я подчеркнул жирным шрифтом, кажется ключевой. Я, конечно, могу ошибаться, но мне кажется, что ОП заявлял, что у него есть таблица «Календарь» с именем «dbo.datestable», и для меня это имеет огромное значение, и теперь я понимаю, почему многие ответы это то, что они включают в себя то, что сгенерировало даты, потому что оно было опубликовано 10 ноября ... через день после окончательного редактирования вопроса, которое удалило последние следы ссылки на "dbo.datestable".
Как я уже сказал, я могу ошибаться, но вот моя интерпретация исходного вопроса.
У меня есть таблица «Календарь» под названием «dbo.datestable». Учитывая любой диапазон дат, охватываемых этой таблицей, как я могу вернуть только даты, которые являются 3-й пятницей каждого месяца в пределах данного диапазона дат?
Поскольку традиционные методы для этого уже были рассмотрены, я добавлю альтернативу, которая может быть полезна для некоторых.
Давайте смоделируем пару столбцов, которые, я думаю, ОП уже будут иметь в своей таблице. Конечно, я угадываю названия столбцов. Пожалуйста, добавьте все эквивалентные столбцы для вашей таблицы "Календарь". Кроме того, я делаю все это в TempDB, поэтому я не рискую вмешиваться в чью-то настоящую таблицу «Календарь».
--=================================================================================================
-- 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)
;
Я также не знаю, может ли ОП вносить изменения в свою таблицу «Календарь», так что это может не помочь ему, но может помочь другим. Имея это в виду, давайте добавим столбец DWoM (день недели для месяца). Если вам не нравится название, пожалуйста, не стесняйтесь сменить его на то, что вам нужно, на свой ящик.
--===== Add the new column.
ALTER TABLE #datestable
ADD DWOM TINYINT NOT NULL DEFAULT (0)
;
Далее нам нужно заполнить новый столбец. У ОП был смысл этого в его оригинальном посте.
--===== 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#
;
Теперь, поскольку это столбец фиксированной длины, который только что создал множество разделений страниц, поэтому нам нужно перестроить кластерный индекс, чтобы «перепаковать» таблицу, чтобы иметь как можно больше строк на страницу для повышения производительности.
--===== "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)
;
Как только это будет сделано, запросы, выполняющие такие действия, как возвращение 3-й пятницы каждого месяца в заданном диапазоне дат, станут тривиальными и достаточно очевидными для чтения.
--===== 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
;