SQL Server 2012 - 在 WHERE 子句下有两个条件的递归 CTE

SQL Server 2012 - Recursive CTE with two conditions under WHERE clause

#1 编辑

我将 WHERE 子句中的 AND 更改为 OR,一切正常。但是我需要在 CTE 的递归部分使用 JOIN,问题又出现了,因为我不能在这里使用 OUTER JOIN。因此,我将 JOIN 更改为 OUTER APPLY 并且效果很好。


SQL Fiddle: http://sqlfiddle.com/#!18/9eecb/81809

我正在尝试实现一个递归 CTE,它接收两个年龄并递增这些年龄,直到它们都等于 120。问题是当我尝试向递归部分添加 WHERE 子句时,谓词被完全忽略:

;with age_cte as (
    select
        26 as wife_age,
        28 as husband_age

    union all

    select
        age_cte.wife_age + 1,
        age_cte.husband_age + 1

    from age_cte

    where wife_age < 120 and husband_age < 120

) select * from age_cte;

一旦其中一个年龄达到 120 岁,CTE 就会停止。例子中,当丈夫的年龄等于120岁时,妻子的年龄为118岁,则停止计算。

我知道数据库遵循查询逻辑。我的问题是我应该如何将正确的逻辑应用于该 CTE,即当一个年龄超过 120 直到另一个年龄达到 120 时 return NULL?

Example:

.     .
.     .
.     .
118   120
119   NULL
120   NULL

我尝试使用带有两个锚点和两个递归部分的 CTE,如下所示 documentation example ("H. Using multiple anchor and recursive members"):

create table age (
    wife_age int,
    husband_age int
);
insert into age values(26, 28);
;with age_cte as (
    -- first anchor
    select
        wife_age
    from age
    union
    -- second anchor
    select
        husband_age
    from age

    union all

    select
        age_cte.wife_age + 1
    from age_cte

    where wife_age < 120

    union all
    --
    select
        age_cte.husband_age + 1
    from age_cte

    where husband_age < 120

) select * from age_cte;

我遗漏了一些东西,因为它在第二个递归查询中为 "husband_age" 提供了 "Invalid column name"。

我也试过这个查询

;with age_cte as (
    select
        26 as wife_age,
        28 as husband_age

    union all

    select
        case when age_cte.wife_age + 1 > 120 then null else age_cte.wife_age + 1 end,
        case when age_cte.husband_age + 1 > 120 then null else age_cte.husband_age + 1 end

    from age_cte

    where 120 >= case 
                     when age_cte.wife_age + 1 < age_cte.husband_age + 1 then
                         age_cte.wife_age + 1
                     else
                         age_cte.husband_age + 1
                 end


) select * from age_cte;

但要么它给出一个无限循环,要么年龄达到 119 永远不会达到 120。

这应该可以满足您的要求:

with age_cte as (
    select 26 as wife_age, 28 as husband_age
    union all
    select
        case when wife_age < 120 then wife_age + 1 end,
        case when husband_age < 120 then husband_age + 1 end
    from age_cte
    where wife_age < 120 or husband_age < 120

) 
select * from age_cte;

即:

  • 你希望在递归查询的 where 子句中使用 or 而不是 and,因此查询一直进行到两个年龄都达到 120

  • 当年龄超过 120

  • 时,您可以在 select 中使用条件逻辑来生成 nulls

Demo on DB Fiddle:

wife_age | husband_age
-------: | ----------:
      26 |          28
      27 |          29
      28 |          30
      29 |          31
...
     116 |         118
     117 |         119
     118 |         120
     119 |        null
     120 |        null