簡體   English   中英

Oracle 如何列出兩個日期之間月份的最​​后幾天

[英]Oracle How to list last days of months between 2 dates

我設法獲得了兩個日期之間的所有日子。 但我想獲得兩個日期之間的所有月份的最后一天(使用一個請求)。

兩個日期之間的所有天數:

 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() - 這使查詢更短,並且再次更標准。

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 級及以上; 對於 level=1 的 START WITH 條件,對於給定日期之間沒有月末的情況(例如,在同一年的 1 月 10 日和 1 月 23 日之間)。

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' 月。然后對於連接,只需獲取所需日期之間的月份。注意:我已轉換為 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;

哪些輸出:

\n |  MONTH_END |\n |  :------------------ |\n |  2020-01-31 00:00:00 |\n |  2021-02-28 00:00:00 |\n |  2021-03-31 00:00:00 |\n |  2021-04-30 00:00:00 |\n |  2021-05-31 00:00:00 |\n |  2022-01-31 00:00:00 |\n |  2022-02-28 00:00:00 |\n

db<> 在這里擺弄

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM