Generate range of dates using CTE Oracle

孤街醉人 提交于 2019-12-12 15:08:30

问题


I want to generate a range of days between two different dates using recursive WITH clause in Oracle.

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

The returned results are completely other than expected:

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

The expected results:

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

Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production.

Edit: As I understood, this is a known Bug in Oracle, the bug exists through Oracle 11.2.0.2 and it was fixed in 11.2.0.3.

Altarnative solution:

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')))

回答1:


This was a known bug in recursive CTE's in Oracle 11 (specifically with regard to date arithmetic). Fixed in Oracle 12. Exactly that behavior: whether you add or subtract in your code, the engine always subtracts, it never adds.

EDIT: Actually, as Alex Poole pointed out in a Comment to the original post, the bug exists through Oracle 11.2.0.2 and it was fixed in 11.2.0.3. End edit

Alas I am not a paying customer, so I can't quote chapter and verse, but with a little bit of Googling you will find links to this (including on OTN where I was involved in a few threads discussing this and other bugs in recursive CTEs - some were fixed, some are still bugs in Oracle 12.1).

Added - here is one of those discussions: https://community.oracle.com/thread/3974408




回答2:


Unless you really need to use the WITH clause there is another solution to get the expected result by using the CONNECT BY clause.

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'));



回答3:


Maybe reverse order of the recursion solves the problem:

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

Note: cast date as date needed due to yet another bug by RCTE in 11gR2

Old discussion here




回答4:


You don't need a recursive WITH clause, you just need a table where number of rows > number of dates you want to generate :

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


来源:https://stackoverflow.com/questions/42509494/generate-range-of-dates-using-cte-oracle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!