单个查询中的多个CTE

axv*_*xvm 38 sql postgresql recursive-query common-table-expression arel

是否可以在单个查询中组合多个CTE arel?我正在寻找获得这样结果的方法:

WITH 'cte1' AS (
...
),
WITH RECURSIVE 'cte2' AS (
...
),
WITH 'cte3' AS (
...
)
SELECT ... FROM 'cte3' WHERE ...
Run Code Online (Sandbox Code Playgroud)

如你所见,我有一个递归CTE和两个非递归.

Erw*_*ter 68

在顶部使用WITH 一次关键字.如果您的任何公用表表达式(CTE)是递归的(rCTE),您还必须RECURSIVE在顶部添加一次关键字,即使并非所有CTE都是递归的:

WITH RECURSIVE
  cte1 AS (...)         -- can still be non-recursive
, cte2 AS (SELECT ...
           UNION ALL
           SELECT ...)  -- recursive term
, cte3 AS (...)
SELECT ... FROM cte3 WHERE ...
Run Code Online (Sandbox Code Playgroud)

手册:

如果RECURSIVE指定,它允许一个SELECT子查询通过名称引用自身.

大胆强调我的.而且,更具洞察力:

另一个影响RECURSIVEWITH不需要对查询进行排序:查询可以引用列表中稍后的另一个查询.(但是,未实现循环引用或相互递归.)如果没有RECURSIVE,WITH查询只能引用列表WITH 中较早的兄弟查询WITH.

再次强调我的重点.这意味着当使用关键词时,WITH子句的顺序是没有意义RECURSIVE.

顺便说一句,由于cte1cte2在实施例未在引用的外SELECT和是普通SELECT命令本身(没有附带影响),它们从未执行(除非引用的cte3).

  • Erwin Trendsetter 阐述了 CTE 知识。+1 - 感谢您的见解! (3认同)

Gor*_*off 21

是.你不重复WITH.你只需使用逗号:

WITH cte1 AS (
...
),
     cte2 AS (
...
),
     cte3 AS (
...
)
SELECT ... FROM 'cte3' WHERE ...
Run Code Online (Sandbox Code Playgroud)

而且:只对字符串和日期常量使用单引号.不要将它们用于列别名.无论如何,它们不被允许用于CTE名称.

  • 但是,如果我需要一个递归CTE与2非递归? (2认同)
  • @萨德克。。。它们可以,没有任何限制,除了需要在使用之前定义 CTE 之外。 (2认同)

Tom*_*ský 6

正如已接受的答案所正确指出的那样,该with子句在每个 CTE 链上仅使用一次。但是,为了完整起见,我想补充一点,它不会阻止您嵌套 CTE

如果cte2使用cte1cte3使用cte2等,则 CTE 之间的依赖链是线性的,并且表示为with3 个 CTE。相反,如果cte2不需要cte1并且两者都只在其中需要,cte3则应考虑将它们嵌套在cte3( with cte3 as (with cte1 as (...), cte2 as (...) select...)) 的定义下。

然后,CTE 的语法反映了 CTE 之间的依赖关系树,并从字面上可视化部分数据集的范围,这可以提高可读性并防止范围泄漏错误。并非所有数据库供应商都支持它,但 Postgres 支持。

例子:

with cte1(id,capital) as (
  values(1,'Prague'),(2,'Bratislava')
), cte2(id,code) as (
  with cte2inner1(id,code) as (
    values(1,'CZ'),(2,'SK')
  ), cte2inner2(id,country) as (
    values(1,'Czech Republic'),(2,'Slovakia')
  )
  select id,country from cte2inner1 join cte2inner2 using (id)
) 
select *
from cte1 join cte2 using (id)
--join cte2inner1  not possible here
Run Code Online (Sandbox Code Playgroud)