使用 CTE Oracle 生成日期范围

Generate range of dates using CTE Oracle

我想在 Oracle 中使用递归 WITH 子句生成两个不同日期之间的天数范围。

WITH CTE_Dates (cte_date) AS
  ( SELECT CAST(TO_DATE('10-02-2017', 'DD-MM-YYYY') AS DATE) cte_date FROM dual
  UNION ALL
  SELECT CAST( (cte_date + 1) AS DATE) cte_date
  FROM CTE_Dates
  WHERE TRUNC(cte_date) + 1 <= TO_DATE('20-02-2017', 'DD-MM-YYYY')
  )
SELECT * FROM CTE_Dates

返回结果与预期完全不同:

10-02-2017
09-02-2017
08-02-2017
07-02-2017
06-02-2017
... (unlimited)

预期结果:

10-02-2017
11-02-2017
...
19-02-2017
20-02-2017

Oracle 数据库 11g 快捷版版本 11.2.0.2.0 - 64 位生产。

编辑: 据我了解,这是 Oracle 中的一个已知错误,该错误存在于 Oracle 11.2.0.2 中,并已在 11.2.0.3 中修复。

备选方案:

SELECT TRUNC (TO_DATE('10-02-2017', 'DD-MM-YYYY') + ROWNUM -1) dt
  FROM DUAL
 CONNECT BY ROWNUM  <= (TO_DATE('20-02-2017', 'DD-MM-YYYY') - (TO_DATE('10-02-2017', 'DD-MM-YYYY')))

您不需要递归 WITH 子句,您只需要 table 其中行数 > 您要生成的日期数:

WITH
 dates
 AS
 (SELECT 
   TO_DATE('10-02-2017', 'DD-MM-YYYY') + (rownum - 1)
  FROM
   all_tables
  WHERE 1=1 
  AND rownum < (TO_DATE('20-02-2017', 'DD-MM-YYYY') - TO_DATE('10-02-2017', 'DD-MM-YYYY')) + 2
 )
SELECT 
 *
FROM
 dates

除非您确实需要使用 WITH 子句,否则还有另一种解决方案可以通过使用 CONNECT BY 子句获得预期结果。

SELECT TO_DATE('10-02-2017', 'DD-MM-YYYY') AS date_range
  FROM dual
UNION ALL
SELECT TO_DATE('10-02-2017', 'DD-MM-YYYY') + LEVEL  
  FROM dual 
  CONNECT BY LEVEL <= (TO_DATE('20-02-2017', 'DD-MM-YYYY') - TO_DATE('10-02-2017', 'DD-MM-YYYY'));

这是 Oracle 11 中递归 CTE 中的一个已知错误(特别是关于日期算法)。已在 Oracle 12 中修复。正是这种行为:无论您在代码中添加还是减去,引擎总是减去,它从不添加。

编辑:实际上,正如 Alex Poole 在对原始 post 的评论中指出的那样,该错误存在于 Oracle 11.2.0.2 中,并已在 11.2 中修复.0.3。 结束编辑

唉,我不是付费客户,所以我不能引用章节和诗句,但是通过一点谷歌搜索你会找到指向这个的链接(包括在 OTN 上,我参与了一些讨论这个话题的话题和递归 CTE 中的其他错误 - 一些已修复,一些仍然是 Oracle 12.1 中的错误)。

已添加 - 这是其中一个讨论:https://community.oracle.com/thread/3974408

也许递归的逆序解决了问题:

with cte_dates (cte_date) as ( 
    select cast(to_date('20-02-2017', 'DD-MM-YYYY') as date) cte_date from dual
    union all
    select  cast((cte_date - 1) as date)  cte_date
    from cte_dates
    where cast(cte_date as date) > to_date('10-02-2017', 'DD-MM-YYYY')
    )
select * from cte_dates 
order by cte_date 
;

2017-02-10
2017-02-11
2017-02-12
...
2017-02-18
2017-02-19
2017-02-20

注意:由于 RCTE 在 11gR2 中的另一个错误

,因此将日期投射为需要的日期

Old discussion here