Вы можете получить начало каждого месяца, охватываемого вашим диапазоном дат; при этом используется пара вымышленных фиксированных значений, поскольку неясно, откуда вы получаете свой диапазон:
select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
from dual
connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'));
MONTH_STAR
----------
2019-03-01
2019-04-01
2019-05-01
2019-06-01
2019-07-01
2019-08-01
2019-09-01
2019-10-01
2019-11-01
9 rows selected.
Затем вы можете манипулировать этими датами различными способами. Вы можете использовать функцию next_day()
, чтобы найти первую субботу месяца - на основе предыдущего дня, если 1-е число месяца является субботой. Если затем добавить к этому 28 дней, вы получите пятую субботу этого месяца - или, если месяцы только с четырьмя субботами, первую субботу в следующем месяце. Таким образом, если вы сравните эти сгенерированные субботы, если они находятся в одном и том же месяце, то в этом месяце должно быть пять суббот.
select month_start,
next_day(month_start - 1, 'SATURDAY') as first_sat,
next_day(month_start - 1, 'SATURDAY') + 28 as poss_fifth_sat,
extract(month from month_start) as month_num,
extract(month from (next_day(month_start - 1, 'SATURDAY') + 28)) as poss_fifth_sat_month
from (
select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
from dual
connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
);
MONTH_STAR FIRST_SAT POSS_FIFTH MONTH_NUM POSS_FIFTH_SAT_MONTH
---------- ---------- ---------- ---------- --------------------
2019-03-01 2019-03-02 2019-03-30 3 3 -- same
2019-04-01 2019-04-06 2019-05-04 4 5
2019-05-01 2019-05-04 2019-06-01 5 6
2019-06-01 2019-06-01 2019-06-29 6 6 -- same
2019-07-01 2019-07-06 2019-08-03 7 8
2019-08-01 2019-08-03 2019-08-31 8 8 -- same
2019-09-01 2019-09-07 2019-10-05 9 10
2019-10-01 2019-10-05 2019-11-02 10 11
2019-11-01 2019-11-02 2019-11-30 11 11 -- same
9 rows selected.
Вам на самом деле не нужно видеть эти значения, я просто показываю работу. Но вы можете использовать два последних вместо фильтра:
select month_start
from (
select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
from dual
connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
)
where extract(month from month_start)
= extract(month from (next_day(month_start - 1, 'SATURDAY') + 28));
MONTH_STAR
----------
2019-03-01
2019-06-01
2019-08-01
2019-11-01
Затем вы можете отформатировать эти даты по своему усмотрению.
Одна возможная проблема (я уверен, что есть и другие) упоминается в документации :
Аргумент char должен быть днем недели на языке даты вашей сессии, либо полным именем, либо сокращением.
Таким образом, приведенный выше код будет работать до тех пор, пока ваш язык даты сеанса английский. Если вы можете быть на 100% уверены, что все, кто работает, всегда будут использовать одни и те же настройки сеанса, тогда вы используете это фиксированное значение (или любой другой язык).
Если вы не можете это контролировать, вы не можете переопределить язык даты, как вы можете с помощью некоторых других функций. В качестве обходного пути вы можете использовать известную субботнюю дату, чтобы получить название или аббревиатуру текущей даты для субботы, без необходимости знать, какой язык на самом деле:
alter session set nls_date_language = 'Spanish';
select month_start
from (
select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
from dual
connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
)
where extract(month from month_start)
= extract(month from (next_day(month_start - 1, to_char(date '2000-01-01', 'Dy')) + 28));
MONTH_STAR
----------
2019-03-01
2019-06-01
2019-08-01
2019-11-01