Как узнать, какие месяцы в годовом диапазоне с 5 субботами между двумя указанными датами согласно входным данным? - PullRequest
1 голос
/ 08 июля 2019

Только через режим запросов, без метода блокировки выполнения.

я пробовал следующее, но не повезло

SELECT
    TO_CHAR(dt, 'mon'),
    COUNT(*) AS noofsat
FROM
    (
        SELECT
            from_date + 1 AS dt
        FROM
            dual
        CONNECT BY
            level <= (
                SELECT
                    TO_DATE - from_date
                FROM
                    dual
            )
    )
WHERE
    TO_CHAR(dt, 'd') = 7
GROUP BY
    TO_CHAR(dt, 'mon')
HAVING
    COUNT(*) = 5

мне нужны только месяц и год, которыеимеет пять суббот, как август 2019

Ответы [ 3 ]

0 голосов
/ 08 июля 2019

Измените ваш источник даты на:

    SELECT
        TO_DATE('20190101','YYYYMMDD') + (LEVEL - 1) AS dt
    FROM
        dual
    CONNECT BY
        level <= 365
0 голосов
/ 08 июля 2019

Вы можете получить начало каждого месяца, охватываемого вашим диапазоном дат; при этом используется пара вымышленных фиксированных значений, поскольку неясно, откуда вы получаете свой диапазон:

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
0 голосов
/ 08 июля 2019

Это очень хороший вопрос, в моей ситуации мне нужно было то же самое, но в SQL Server, и вот как я это сделал, я опубликую ответ для тех, кто в конечном итоге ищет решение для SQL Server.

Я закончил создание измерения даты в моем хранилище данных и запросил его:

DROP TABLE IF EXISTS ##Dates


 CREATE TABLE ##Dates(DateValue Date, 
                      YearValue INT, 
                      MonthValue INT, 
                      DayValue INT,
                      WeekDayValue INT)


  DECLARE @start DATE = '2019-01-01'
  WHILE @start < '2019-12-31'
  BEGIN
    INSERT INTO  ##Dates(DateValue,
                         YearValue, 
                         MonthValue, 
                         DayValue,
                         WeekDayValue)
    VALUES(@start,
            DATEPART(YY,@start), 
            DATEPART(mm,@start), -- In oracle TO_CHAR(dt, 'mon')
            DATEPART(dd,@start),
            DATEPART(dw,@start)) --In oracle  TO_CHAR(dt, 'd')

    SET @start = DATEADD(dd,1,@start)
  END



  SELECT MonthValue, SUM(CASE WHEN WeekDayValue = 7 THEN 1 ELSE 0 END) AS NumOfSaturdays FROM ##Dates
WHERE DateValue BETWEEN '2019-01-01' AND '2019-12-01'
GROUP BY MonthValue
HAVING SUM(CASE WHEN WeekDayValue = 7 THEN 1 ELSE 0 END) > 4

Вывод:

MonthValue  NumOfSaturdays
3            5
6            5
8            5
11           5
...