问题描述
我设法获得2个日期之间的所有日期。 但我想获得两个日期之间的所有月份的最后一天(使用一个请求)。
两个日期之间的所有天:
select to_date('01/01/2000','dd/mm/yyyy') + (LEVEL-1) as jour
from dual
connect by level <= to_date('31/12/2050','dd/mm/yyyy')-to_date('01/01/2000','dd/mm/yyyy')
当月最后一天:
select LAST_DAY(sysdate) FROM dual
我不知道如何混合两者并获得预期结果:
20000131
20000228
20000331
etc...
解决方法
我想是DISTINCT
+ LAST_DAY
。
设置日期格式(以使其与您的日期格式匹配;或者,将TO_CHAR
应用于具有适当格式掩码的jour
值):
SQL> alter session set nls_Date_format = 'yyyymmdd';
Session altered.
我缩短了时间跨度至2年(以节省空间:))。
SQL> select distinct last_day(to_date('01/01/2000','dd/mm/yyyy') + (LEVEL-1)) as jour
2 from dual
3 connect by level <= to_date('31/12/2002','dd/mm/yyyy')-to_date('01/01/2000','dd/mm/yyyy')
4 order by 1;
JOUR
--------
20000131
20000229
20000331
20000430
20000531
20000630
20000731
20000831
<snip>
20020630
20020731
20020831
20020930
20021031
20021130
20021231
36 rows selected.
SQL>
,
我喜欢使用标准的递归查询,而不是Oracle的特定CONNECT BY
语法。在这里,您可以枚举月份的开头,然后偏移到月份的结尾:
with cte (dt) as (
select date '2020-01-01' dt from dual
union all
select dt + interval '1' month from cte where dt + interval '1' month < date '2051-01-01'
)
select last_day(dt) dt from cte order by dt
请注意,它使用的是标准日期文字(date 'YYYY-MM-DD'
)而不是to_date()
-这会使查询更短,并且再次使查询更为标准。
| DT | | :-------- | | 31-JAN-20 | | 29-FEB-20 | | 31-MAR-20 | | 30-APR-20 | | 31-MAY-20 | ... | 31-OCT-50 | | 30-NOV-50 | | 31-DEC-50 |,
您可以使用CONNECT BY查询来执行此操作。 (您也可以使用递归查询来执行此操作,例如GMB提出的方法,但是必须对其进行调整以解决您提出的问题-它应该允许输入开始和结束日期,如果没有输入,则应该返回零行两个日期之间的月份结束。)
在下面的查询中,我使用WITH子句给出开始和结束日期。在您的问题中,它们更有可能是绑定变量。 (或者它们是从桌子上读取的?)
请注意START WITH子句。 CONNECT BY条件仅适用于2级及更高级别;如果在给定日期之间(例如,同年1月10日至1月23日之间)没有月末,则需要为level = 1使用START WITH条件。
with
input_dates(start_dt,end_dt) as (
select date '2020-01-22',date '2020-04-03' from dual
)
select add_months(last_day(start_dt),level - 1) as eom
from input_dates
start with last_day(start_dt) <= end_dt
connect by add_months(last_day(start_dt),level - 1) <= end_dt
;
EOM
----------
2020-01-31
2020-02-29
2020-03-31
,
您的初始查询只需要进行一些调整。而不是仅仅将级别1(每天结束)转换为每月增量“级别1)*间隔“ 1”个月。然后通过在所需日期之间获取month_between进行连接。注意:我已转换为ISO日期的标准格式,而不是to_date函数。使查询更短,更易于阅读。
select last_day(date '2000-01-01' + (level-1)*interval '1' month) as jour
from dual
connect by level <= 1+months_between(date '2050-12-31',date '2000-01-01');
,
您可以使用递归子查询。
这将适用于:
- 多个输入范围;
- 当开始日期在月底时输入;
- 当范围不包含任何月末时。
WITH input_ranges ( start_date,end_date ) AS (
-- Should return a single row.
SELECT DATE '2020-01-31',DATE '2020-02-01' FROM DUAL UNION ALL
-- Should return multiple rows.
SELECT DATE '2021-02-01',DATE '2021-06-01' FROM DUAL UNION ALL
-- Should not return any rows as there is no end of the month in the range.
SELECT DATE '2021-10-06',DATE '2021-10-20' FROM DUAL UNION ALL
-- Should work even though February does not have 30 days.
SELECT DATE '2022-01-30',DATE '2022-03-02' FROM DUAL
),month_ends ( month_end,end_date ) AS (
SELECT LAST_DAY( start_date ),end_date
FROM input_ranges
WHERE LAST_DAY( start_date ) <= end_date
UNION ALL
SELECT ADD_MONTHS( month_end,1 ),end_date
FROM month_ends
WHERE ADD_MONTHS( month_end,1 ) <= end_date
)
SELECT month_end
FROM month_ends
ORDER BY month_end;
输出:
| MONTH_END | | :------------------ | | 2020-01-31 00:00:00 | | 2021-02-28 00:00:00 | | 2021-03-31 00:00:00 | | 2021-04-30 00:00:00 | | 2021-05-31 00:00:00 | | 2022-01-31 00:00:00 | | 2022-02-28 00:00:00 |
dbfiddle here