Create all months list from a date column in ORACLE SQL

≡放荡痞女 提交于 2021-02-08 10:08:14

问题


CREATE TABLE dates(
alldates date);

INSERT INTO dates  (alldates) VALUES ('1-May-2017');
INSERT INTO dates  (alldates) VALUES ('1-Mar-2018');

I want to generate all months beginning between these two dates. I am very new to Oracle SQL. My solution is below, but it is not working properly.

WITH t1(test)  AS (
SELECT MIN(alldates) as test  
FROM dates 
UNION ALL 
SELECT ADD_MONTHS(test,1) as test
FROM t1 
WHERE t1.test<= (SELECT MAX(alldates) FROM date)                 
)
SELECT * FROM t1

The result I want should look like Test

2017-02-01
2017-03-01
...
2017-12-01
2018-01-01
2018-02-01
2018-03-01


回答1:


You made a typo and wrote date instead of dates but you also need to make a second change and use ADD_MONTHS in the recursive query's WHERE clause or you will generate one too many rows.

WITH t1(test)  AS (
  SELECT MIN(alldates)
  FROM dates 
UNION ALL 
  SELECT ADD_MONTHS(test,1)
  FROM t1 
  WHERE ADD_MONTHS(test,1) <= (SELECT MAX(alldates) FROM dates)
)
SELECT * FROM t1

Which outputs:

| TEST      |
| :-------- |
| 01-MAY-17 |
| 01-JUN-17 |
| 01-JUL-17 |
| 01-AUG-17 |
| 01-SEP-17 |
| 01-OCT-17 |
| 01-NOV-17 |
| 01-DEC-17 |
| 01-JAN-18 |
| 01-FEB-18 |
| 01-MAR-18 |

However, a more efficient query would be to get the minimum and maximum values in the same query and then iterate using these pre-found bounds:

WITH t1(min_date, max_date)  AS (
  SELECT MIN(alldates),
         MAX(alldates)  
  FROM   dates 
UNION ALL 
  SELECT ADD_MONTHS(min_date,1),
         max_date
  FROM   t1 
  WHERE  ADD_MONTHS(min_date,1) <= max_date
)
SELECT min_date AS month
FROM   t1

db<>fiddle here


Update

Oracle 11gR2 has bugs handling recursive date queries; this is fixed in later Oracle versions but if you want to use SQL Fiddle and Oracle 11gR2 then you need to iterate over a numeric value and not a date. Something like this:

SQL Fiddle

Oracle 11g R2 Schema Setup:

CREATE TABLE dates(
alldates date);

INSERT INTO dates  (alldates) VALUES ('1-May-2017');
INSERT INTO dates  (alldates) VALUES ('1-Mar-2018');

Query 1:

WITH t1(min_date, month, total_months)  AS (
  SELECT MIN(alldates),
         0,
         MONTHS_BETWEEN(MAX(alldates),MIN(alldates))
  FROM   dates 
UNION ALL 
  SELECT min_date,
         month+1,
         total_months
  FROM   t1 
  WHERE  month+1<=total_months
)
SELECT ADD_MONTHS(min_date,month) AS month
FROM   t1

Results:

|                MONTH |
|----------------------|
| 2017-05-01T00:00:00Z |
| 2017-06-01T00:00:00Z |
| 2017-07-01T00:00:00Z |
| 2017-08-01T00:00:00Z |
| 2017-09-01T00:00:00Z |
| 2017-10-01T00:00:00Z |
| 2017-11-01T00:00:00Z |
| 2017-12-01T00:00:00Z |
| 2018-01-01T00:00:00Z |
| 2018-02-01T00:00:00Z |
| 2018-03-01T00:00:00Z |



回答2:


You seem to want a recursive CTE. That syntax would be:

WITH CTE(min_date, max_date) as (
      SELECT MIN(alldates) as min_date, MAX(alldates) as max_date
      FROM dates
      UNION ALL
      SELECT add_months(min_date, 1), max_date
      FROM CTE
      WHERE min_date < max_date
     )
SELECT min_date
FROM CTE;

Here is a db<>fiddle.




回答3:


You just made a typo: date instead of dates:

WITH t1(test)  AS (
SELECT MIN(alldates) as test  
FROM dates 
UNION ALL 
SELECT ADD_MONTHS(test,1) as test
FROM t1 
WHERE t1.test<= (SELECT MAX(alldates) FROM dateS)   -- fixed here     
)
SELECT * FROM t1


来源:https://stackoverflow.com/questions/63876267/create-all-months-list-from-a-date-column-in-oracle-sql

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