Oracle - 执行递归 'WITH' 查询时检测到循环

Oracle - Cycle detected while executing recursive 'WITH' query

我正在使用 oracle sql 做一个递归查询的基本示例。我正在计算格式 MON-YY 的未来月份。我设法得到了一个看似正确的查询,但我不明白 WITH 查询的中断条件。

我试图打破年份值(例如,当你到达 2020 年时停止),但它在这样做时检测到一个周期。如果我打破月份值(例如 12 月),它会起作用。

这是我基于一个月的休息时间的查询:

with
    prochains_mois(mois, annee) as (
        select 'sep' as mois, 19 as annee
        from dual
    union all
        select 
            case mois
                when 'jan' then 'fev'
                when 'fev' then 'mar'
                when 'mar' then 'avr'
                when 'avr' then 'mai'
                when 'mai' then 'jun'
                when 'jun' then 'jui'
                when 'jui' then 'aou'
                when 'aou' then 'sep'
                when 'sep' then 'oct'
                when 'oct' then 'nov'
                when 'nov' then 'dec'
                when 'dec' then 'jan'
            end,
            case mois
                when 'dec' then annee + 1
                else annee
            end
        from prochains_mois r
        where mois <> 'dec'
    )
select * from prochains_mois;

如果我这样做,它 returns 一个一致的结果。

MOI      ANNEE
--- ----------
sep         19
oct         19
nov         19
dec         19

现在,如果我尝试打破年份的递归查询,假设是 2020 年,那么我将 with 子句中的 where 条件更改为:

where annee < 20

然后我得到:

ORA-32044: cycle detected while executing recursive WITH query

我试了一个月后断了,看看我的年份加法是否正确,似乎是这样。如果我在 3 月中断,我会正确地得到 1 月和 2 月:

where mois <> 'mar'

给予

MOI      ANNEE
--- ----------
sep         19
oct         19
nov         19
dec         19
jan         20
fev         20
mar         20

您的主要问题是您试图使用 strings/numbers 来操纵日期。不要那样做;如果您使用日期,请使用日期!

例如你可以像这样做你想做的事:

WITH prochains_mois (mnth_dt) AS (SELECT TRUNC(sysdate, 'mm') mnth_dt
                                  FROM   dual
                                  UNION ALL
                                  SELECT add_months(mnth_dt, 1) mnth_dt
                                  FROM   prochains_mois
                                  WHERE add_months(mnth_dt, 1) < add_months(TRUNC(sysdate, 'yyyy'), 12))
SELECT mnth_dt,
       to_char(mnth_dt, 'mon') mois,
       to_char(mnth_dt, 'yy') annee
FROM   prochains_mois;

MNTH_DT     MOIS ANNEE
----------- ---- -----
01/09/2019  sep  19
01/10/2019  oct  19
01/11/2019  nov  19
01/12/2019  dec  19

N.B。您可以将递归子分解查询中的谓词简化为 mnth_dt < add_months(TRUNC(SYSDATE, 'yyyy'), 11).

这是通过获取开始日期(在这里,我使用了 sysdate)并找到该月的第一天(通过使用可选的第二个参数 TRUNC 来指定我们的级别)来实现的正在将其截断为)。

然后我们只需在每个日期上加一个月,直到到达开始日期所在年份的最后一个月。

只有在您获得日期后,您才能使用 to_char.

以您需要的格式输出数据

使用DATEs:

with prochains_mois( value ) as (
  select DATE '2019-09-01' from dual
union all
  select ADD_MONTHS( value, 1 )
  FROM   prochains_mois
  WHERE  value < DATE '2020-12-01'
)
select SUBSTR( TO_CHAR( value, 'mon', 'NLS_DATE_LANGUAGE=FRENCH' ), 1, 3 ) AS mois,
       TO_CHAR( value, 'RR' ) AS annee
from   prochains_mois;

输出:

MOIS | ANNEE
:--- | :----
sep  | 19   
oct  | 19   
nov  | 19   
dec  | 19   
jan  | 20   
fev  | 20   
mar  | 20   
avr  | 20   
mai  | 20   
jui  | 20   
jui  | 20   
aou  | 20   
sep  | 20   
oct  | 20   
nov  | 20   
dec  | 20   

或使用您的查询并检查月份 年份是否不匹配:

with
    prochains_mois(mois, annee) as (
        select 'sep' as mois, 19 as annee
        from dual
    union all
        select 
            case mois
                when 'jan' then 'fev'
                when 'fev' then 'mar'
                when 'mar' then 'avr'
                when 'avr' then 'mai'
                when 'mai' then 'jun'
                when 'jun' then 'jui'
                when 'jui' then 'aou'
                when 'aou' then 'sep'
                when 'sep' then 'oct'
                when 'oct' then 'nov'
                when 'nov' then 'dec'
                when 'dec' then 'jan'
            end,
            case mois
                when 'dec' then annee + 1
                else annee
            end
        from prochains_mois r
        where ( mois, annee ) NOT IN ( ( 'dec', 20 ) )
    )
select * from prochains_mois;

输出:

MOIS | ANNEE
:--- | ----:
sep  |    19
oct  |    19
nov  |    19
dec  |    19
jan  |    20
fev  |    20
mar  |    20
avr  |    20
mai  |    20
jun  |    20
jui  |    20
aou  |    20
sep  |    20
oct  |    20
nov  |    20
dec  |    20

db<>fiddle here