在Oracle PL/SQL中为每个组生成日期范围之间的月度期间

wa7juj8i  于 2023-06-22  发布在  Oracle
关注(0)|答案(4)|浏览(132)

我在How to get monthly periods between a date range in Oracle PL/SQL中遇到了同样的问题。但我需要生成每个组的日期范围之间的所有月度期间。我如何修改上面的决定?
因此,我期望这样的结果:如果A组的日期范围为(01/01/2023-03/10/2023),则B组的日期范围为(01/05/2023-05/12/2023)

Group      Start            End
A   -    01/01/2023   -   01/31/2023
A   -    02/01/2023   -   02/28/2023
A   -    03/01/2023   -   03/10/2023

B   -    01/05/2023   -   01/31/2023
B   -    02/01/2023   -   02/28/2023
B   -    03/01/2023   -   03/31/2023
B   -    04/01/2023   -   04/30/2023
B   -    05/01/2023   -   05/12/2023
juud5qan

juud5qan1#

这是一个 * 行生成器 * 技术问题; case表达式用于区分整个周期中的第一个和最后一个日期。就像这样:
样本数据:

SQL> alter session set nls_date_Format = 'mm/dd/yyyy';

Session altered.

SQL> with ranges (grp, range_From, range_to) as
  2    (select 'A', date '2023-01-01', date '2023-03-10' from dual union all
  3     select 'B', date '2023-01-05', date '2023-05-12' from dual
  4    ),

查询从这里开始; temp CTE有助于简化 main 查询,因为它计算范围日期之间的月份。没有它也可以完成,但是查询比它应该的更复杂(在我看来):

5  temp as
  6    (select grp,
  7       range_from,
  8       range_to,
  9       months_between(trunc(range_to, 'mm'), trunc(range_From, 'mm')) + 1 mon_bet
 10     from ranges
 11    )
 12  select grp,
 13    case when column_value = 1 then range_from
 14         else trunc(add_months(range_from, column_value - 1), 'mm')
 15    end start_date,
 16    --
 17    case when column_value = mon_bet then range_to
 18         else last_day(add_months(range_from, column_value - 1))
 19    end end_date
 20  from temp cross join
 21    table(cast(multiset(Select level from dual
 22                        connect by level <= mon_bet
 23                       ) as sys.odcinumberlist))
 24  order by grp, start_date;

G START_DATE END_DATE
- ---------- ----------
A 01/01/2023 01/31/2023
A 02/01/2023 02/28/2023
A 03/01/2023 03/10/2023
B 01/05/2023 01/31/2023
B 02/01/2023 02/28/2023
B 03/01/2023 03/31/2023
B 04/01/2023 04/30/2023
B 05/01/2023 05/12/2023

8 rows selected.

SQL>
dfty9e19

dfty9e192#

您可以使用递归查询和ADD_MONTHS函数重复添加一个月:

WITH months (id, dt_from, dt_to, dt_end) AS (
  SELECT id,
         dt_from,
         ADD_MONTHS(TRUNC(dt_from, 'MM'), 1),
         dt_to
  FROM   table_name
UNION ALL
  SELECT id,
         dt_to,
         ADD_MONTHS(dt_to, 1),
         dt_end
  FROM   months
  WHERE  dt_to < dt_end
)
SEARCH DEPTH FIRST BY id SET order_id
SELECT id,
       dt_from,
       LEAST(dt_to - INTERVAL '1' SECOND, dt_end) AS dt_to
FROM   months;

其中,对于样本数据:

CREATE TABLE table_name (id, dt_from, dt_to) AS
SELECT 'A', DATE '2023-01-01', DATE '2023-03-10' FROM DUAL UNION ALL
SELECT 'B', DATE '2023-01-05', DATE '2023-05-12' FROM DUAL;

输出:
| ID| DT_FROM| DT_TO|
| - -----|- -----|- -----|
| 一个|2019 - 01 - 21 00:00:00| 2023 - 01 - 31 23:59:59|
| 一个|2019 - 02 - 21 00:00:00| 2023 - 02 - 28 23:59:59|
| 一个|2019 - 03 - 01 00:00:00| 2019 - 03 - 10 00:00:00|
| B级|2019 - 01 - 05 00:00:00| 2023 - 01 - 31 23:59:59|
| B级|2019 - 02 - 21 00:00:00| 2023 - 02 - 28 23:59:59|
| B级|2019 - 03 - 01 00:00:00| 2023 - 03 - 31 23:59:59|
| B级|2019 - 04 - 01 00:00:00| 2023 - 04 - 30 23:59:59|
| B级|2023 - 05 - 01 00:00:00| 2019 - 05 - 12 00:00:00|

  • 注意:在Oracle中,DATE总是有一个时间组件(您的客户端应用程序可能默认只显示日期,而不显示时间组件,但这并不意味着它们不存在;只是没有显示出来)。

fiddle

byqmnocz

byqmnocz3#

我已经明白了,在connect by level之后只需要使用group by。所有级别都将自动正确。

v440hwme

v440hwme4#

我还建议分析以下示例:
DBFiddle:https://dbfiddle.uk/A0RbUvJh

select t.id
      ,greatest(t.dt_from, trunc   (add_months(t.dt_from,n-1),'mm')) dt_from
      ,least   (t.dt_to  , last_day(add_months(t.dt_from,n-1))) as dt_to
from table_name t
    ,xmltable(
        '0 to xs:integer($M)' 
        passing floor(months_between(dt_to,dt_from)) as M
        columns n for ordinality
    )

简短说明:

  • 我们可以使用xmltable('a to b')作为一个简单的生成器,例如:
SQL> select * from xmltable('3 to 10' columns x for ordinality, y int path '.');

         X          Y
---------- ----------
         1          3
         2          4
         3          5
         4          6
         5          7
         6          8
         7          9
         8         10

8 rows selected.

相关问题