postgresql 如何在一个SQL查询中使用多个CTE?

fquxozlt  于 2022-11-04  发布在  PostgreSQL
关注(0)|答案(4)|浏览(348)

是否可以在单个查询中合并多个CTE?
我寻找的办法得到这样的结果:

WITH cte1 AS (
...
),
WITH RECURSIVE cte2 AS (
...
),
WITH cte3 AS (
...
)
SELECT ... FROM cte3 WHERE ...

如您所见,我有一个递归CTE和两个非递归CTE。

6yoyoihd

6yoyoihd1#

在顶部使用关键字WITHonce。如果任何公用表表达式(CTE)是递归的(rCTE),则必须在顶部添加关键字RECURSIVE *once *,即使并非所有CTE都是递归的:

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

本手册:
如果指定了RECURSIVE,则允许SELECT子查询按名称引用自身。
大胆强调我的观点。更有见地的是:

RECURSIVE的另一个效果是WITH查询不需要排序:查询可以引用列表中较后的另一个查询。(然而,循环引用或相互递归不被实现。)如果没有RECURSIVEWITH查询只能引用WITH列表中较早的兄弟WITH查询。

再次加粗强调mine。意思是当使用了RECURSIVE关键词时,WITH子句的顺序是 * 无意义的 *。
顺便说一句,由于示例中的cte1cte2没有在外部SELECT中引用,并且它们本身是普通的SELECT命令(没有附带影响),因此它们永远不会被执行(除非在cte3中引用)。

mo49yndu

mo49yndu2#

是的。你不用重复WITH。你只用一个逗号:

WITH cte1 AS (
...
),
     cte2 AS (
...
),
     cte3 AS (
...
)
SELECT ... FROM 'cte3' WHERE ...

And:仅对字符串常量和日期常量使用单引号。不要对列别名使用单引号。无论如何,CTE名称都不允许使用单引号。

yxyvkwin

yxyvkwin3#

正如公认的正确答案所说,with子句在每个CTE链中只使用一次。但是,为了完整起见,我想添加 * 它不会阻止您嵌套CTE *。
如果cte2使用cte1cte3使用cte2等,则CTE之间的相关性链是线性的,并且其被表示为具有3个CTE的with。相反,如果cte2不需要cte1,并且两者都只在cte3中需要,则应考虑将它们嵌套在cte3的定义下(with cte3 as (with cte1 as (...), cte2 as (...) select...))的数据。
CTE的语法反映了CTE之间的依赖关系树,并从字面上可视化了部分数据集的作用域,这可以提高可读性并防止作用域泄漏错误。不是所有的数据库供应商都支持它,但Postgres支持。
Example:

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
cgvd09ve

cgvd09ve4#

问题原因:在这里,你不必使用多个WITH子句来合并Multiple CTE。
解决方案:可以在SQL中使用单个WITH子句创建多个公用表表达式。使用单个WITH子句创建两个不同的CTE,并以逗号分隔以创建多个CTE。
使用单个样品对多个CTE进行采样

With EmpCount1(DeptName,TotalEmployees)
as
  (
   Select DeptName, COUNT(*) as TotalEmployees
   from Tbl_EmpDetails
   join Tbl_Dept Dept
   on Tbl_EmpDetails.DeptId = Dept.DeptId
   WHERE DeptName IN ('BI','DOTNET')
   group by DeptName
  ),
EmpCount2(DeptName,TotalEmployees)
as
  (
   Select DeptName, COUNT(*) as TotalEmployees
   from Tbl_EmpDetails
   join Tbl_Dept Dept
   on Tbl_EmpDetails.DeptId = Dept.DeptId
   WHERE DeptName IN ('JAVA','AI')
   group by DeptName
  )

  Select * from EmpCount1
  UNION
  Select * from EmpCount2

这是使用单个With子句创建多个公用表表达式的示例语法。

相关问题