【问题标题】:ORACLE SQL: Create new row on the basis of date rangeORACLE SQL:根据日期范围创建新行
【发布时间】:2021-07-22 05:20:20
【问题描述】:

例如,我有一个表名 test_cross_months,数据如下:

id start_date end_date
44 2020-01-04 2020-01-04
44 2020-01-30 2020-02-10
44 2020-02-27 2020-03-03

预期结果:

id start_date end_date
44 2020-01-04 2020-01-04
44 2020-01-30 2020-01-31
44 2020-02-01 2020-02-10
44 2020-02-27 2020-02-29
44 2020-03-01 2020-03-03

所以对于 |44|2020-01-30 |2020-02-10| 应该有两行,分别是 2020 年 1 月 30 日到 2020 年 1 月 31 日和 2020 年 2 月 1 日到 2020 年 2 月 10 日

我尝试将结束日期与 start_date 的最后一天进行比较,但遇到问题,因为没有为 end_date 范围创建新行。

有没有人可以提出解决方案?

【问题讨论】:

  • 如果44 2020-02-27 2020-04-03 怎么办?
  • 那么它将是 2020-02-27 2020-02-29 2020-03-01 2020-03-31 2020-04-01 2020-04-03
  • 非常感谢您的回答,我也会尝试以下 SQL。

标签: sql oracle date


【解决方案1】:

您可以使用递归查询(无论您的范围跨越多少个月,它都会起作用):

WITH months ( id, start_date, end_date, final_date ) AS (
  SELECT id,
         start_date,
         LEAST( LAST_DAY( start_date ), end_date ),
         end_date
  FROM   table_name
UNION ALL
  SELECT id,
         end_date + INTERVAL '1' DAY,
         LEAST( ADD_MONTHS( end_date, 1 ), final_date ),
         final_date
  FROM   months
  WHERE  end_date < final_date
)
SEARCH DEPTH FIRST BY final_date SET dt_order
SELECT id,
       start_date,
       end_date
FROM   months;

其中,对于样本数据:

CREATE TABLE table_name (id, start_date, end_date) AS
SELECT 44, DATE '2020-01-04', DATE '2020-01-04' FROM DUAL UNION ALL
SELECT 44, DATE '2020-01-30', DATE '2020-02-10' FROM DUAL UNION ALL
SELECT 44, DATE '2020-02-27', DATE '2020-03-03' FROM DUAL;

输出:

ID START_DATE END_DATE
44 2020-01-04 00:00:00 2020-01-04 00:00:00
44 2020-01-30 00:00:00 2020-01-31 00:00:00
44 2020-02-01 00:00:00 2020-02-10 00:00:00
44 2020-02-27 00:00:00 2020-02-29 00:00:00
44 2020-03-01 00:00:00 2020-03-03 00:00:00

db小提琴here

【讨论】:

    【解决方案2】:

    使用数字表和日期算术

    -- example of table of numbers
    with nmbrs(n) as(
       select 0 from dual union all
       select 1 from dual union all
       select 2 from dual 
    )
    select t.id, 
       case when n=0 then t.start_date else trunc(t.start_date, 'MM') + NUMTOYMINTERVAL(n, 'MONTH') end s,
       case when n=MONTHS_BETWEEN(last_day(t.end_date), last_day(t.start_date)) then t.end_date 
           else last_day(t.start_date + NUMTOYMINTERVAL(n, 'MONTH')) end e
    from test_cross_months t
    join nmbrs on nmbrs.n <= MONTHS_BETWEEN(last_day(t.end_date), last_day(t.start_date))
    order by t.id, s 
    

    db<>fiddle

    【讨论】:

    • 如果范围超过 3 个月会怎样?还是50个月?还是900个月?您有一个静态的数字范围,这种方法要求您始终知道您将遇到的日期范围的最大大小,否则查询将不会返回足够的行。
    • 从业务角度来看,月不会跨越4到5个月。最大日期范围为 1 年,但我认为最终用户也不会这样做。非常感谢您的帮助。
    • @MT0,这只是一个示例数字表。一个人可能在数据库中有一个包含数百万行的实例化表,或者根据输入动态生成它,或者只是在查询中具有应用程序合理的一组固定数字,如示例中所示。
    猜你喜欢
    • 2016-10-25
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-15
    • 1970-01-01
    • 2011-03-29
    相关资源
    最近更新 更多