Oracle如何列出两个日期之间的月份的最后几天

问题描述

我设法获得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()-这会使查询更短,并且再次使查询更为标准。

Demo on DB Fiddle

| 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

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...