#sql #oracle #date-arithmetic
#sql #Oracle #дата-арифметика
Вопрос:
У меня есть следующий запрос, который выдает вторую и четвертую субботы предыдущего месяца вместе со всеми воскресеньями предыдущего месяца:
SELECT to_char(NEXT_DAY(NEXT_DAY(NEXT_DAY(NEXT_DAY(TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH') - 1, 'SATURDAY'), 'SATURDAY'),'SATURDAY'),'SATURDAY'),'YYYYMMDD') SECOND_SATURDAY
FROM DUAL
UNION ALL
SELECT to_char(NEXT_DAY(NEXT_DAY(TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH') - 1, 'SATURDAY'),'SATURDAY'),'YYYYMMDD') SECOND_SATURDAY
FROM DUAL
UNION ALL
select distinct day_date from
(SELECT to_char(NEXT_DAY(LEVEL TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH') - 1,'SUNDAY'),'YYYYMMDD') day_date
FROM DUAL
CONNECT BY LEVEL <= ADD_MONTHS(TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH'), 1) - TRUNC((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL), 'MONTH'))
where substr(day_date,1,6) in (select to_char((SELECT LAST_DAY(ADD_MONTHS(sysdate,-1)) FROM DUAL),'YYYYMM') from dual)
Но я чувствую, что должен быть более простой способ получить тот же результат в oracle.Любая помощь в этом отношении приветствуется. Мое требование к формату даты — ‘ГГГГММДД’.
Ответ №1:
Отредактировано, потому что я неправильно понял вопрос:
Я думаю, вы хотите что-то вроде следующего:
SELECT TO_CHAR(my_date, 'YYYYMMDD') AS my_formatted_date
FROM (
SELECT NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2)) 7, 'SATURDAY') AS my_date
FROM dual
UNION ALL
SELECT NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2)) 21, 'SATURDAY')
FROM dual
UNION ALL
SELECT NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2)), 'SUNDAY') (LEVEL-1)*7
FROM dual
CONNECT BY NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2)), 'SUNDAY') (LEVEL-1)*7 < TRUNC(SYSDATE, 'MONTH')
);
В первой части запроса я определяю последний день предыдущего месяца, а затем получаю первую субботу, которая выпадает по крайней мере через неделю после этой даты (вторая суббота месяца будет где-то с 8 по 14 число). Тогда я получаю, что первая суббота выпадает по крайней мере через три недели после последнего дня предыдущего месяца (четвертая суббота будет где-то с 22 по 28-е). Наконец, я перебираю воскресенья предыдущего месяца, начиная с первого воскресенья, приходящегося на последний день месяца, предшествующего этому (то есть два месяца назад). Вы также могли бы использовать NEXT_DAY(TRUNC(ADD_MONTHS(SYSDATE, -1) - 1), 'SUNDAY')
вместо NEXT_DAY(LAST_DAY(ADD_MONTHS(SYSDATE, -2)), 'SUNDAY')
MY_FORMA
--------
20190209
20190223
20190203
20190210
20190217
20190224
6 rows selected.
Комментарии:
1. Здесь субботы относятся к текущему месяцу. Я предполагаю, что я должен использовать (SYSDATE, -2) вместо (SYSDATE, -1)
2. Привет @katipra, я полагаю, что я неправильно понял ваш вопрос. Да, используйте
ADD_MONTHS(SYSDATE, -2)
вместоADD_MONTHS(SYSDATE, -1)
, чтобы получить необходимый ответ.
Ответ №2:
Например, как здесь:
with
t1 as (select add_months(trunc(sysdate, 'month'), -1) dt from dual),
t2 as (
select dt level - 1 dt, to_char(dt level - 1, 'dy', 'nls_date_language=english') dy
from t1 connect by dt level - 1 < trunc(sysdate, 'month'))
select to_char(dt, 'yyyymmdd') dt, dy
from (
select dt, dy, sum(case when dy = 'sat' then 1 end) over (order by dt) sm from t2)
where dy = 'sun' or (dy = 'sat' and sm in (2, 4))
Результат:
DT DY
-------- ---
20190203 sun
20190209 sat
20190210 sun
20190217 sun
20190223 sat
20190224 sun
Я сгенерировал все дни за предыдущий месяц, присвоил дням английские названия, условно посчитал субботы и показал только интересные даты.
Редактировать:
Это работает, но есть ли какой-либо способ обойти предложение with, поскольку мой родительский запрос имеет значение from: with x as ( …. где date в («требуемые дни») и …) . Таким образом, это представляет собой вложенную ситуацию with .
Да, вы можете легко преобразовать это, например, как здесь:
select to_char(dt, 'yyyymmdd') dt
from (
select dt, dn, rank() over (order by mod(dn, 6), dt) rnk
from (
select d level - 1 dt, d level - trunc(d level - 1, 'iw') dn
from (select add_months(trunc(sysdate, 'month'), -1) d from dual)
connect by level <= add_months(d, 1) - d))
where dn = 7 or (dn = 6 and rnk in (2, 4))
with
предложение сделало шаги более удобочитаемыми. Теперь я также использовал номера дней и mod()
для подсчета, просто чтобы показать разные подходы, но вы можете использовать все, что вам понятнее (названия дней, sum
или count
вместо rank
).
Комментарии:
1. Это работает, но есть ли какой-либо способ обойти предложение with, поскольку мой родительский запрос имеет значение from: with x as ( …. где дата в («требуемые дни») и …) . Таким образом, это представляет собой вложенную ситуацию with