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

发布于 2025-01-18 14:27:25 字数 206 浏览 3 评论 0原文

是否可以将多个CTE组合在单个查询中?

我正在寻找这样的结果:

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

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

Is it possible to combine multiple CTEs in single query?

I am looking for way to get result like this:

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

As you can see, I have one recursive CTE and two non recursive.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

℡寂寞咖啡 2025-01-25 14:27:25

在顶部使用 的关键词。如果您的任何常见表表达式(CTE)是递归(RCTE),则必须在top 上添加关键字递归,即使并非所有CTE都是递归:

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

<

如果指定递归,则允许 a 选择子查询到
用名称引用自己。

大胆的重点是我的。而且,更具洞察力:

递归的另一个效果是,不必订购
查询可以引用列表以后的另一个查询。 (然而,
未实施循环引用或相互递归。)
没有递归,查询只能用引用
列表中中较早的查询。


再次强调我的。这意味着使用条款的的顺序是 时使用递归键>关键词。

顺便说一句,由于cte1cte2在示例中未引用select和plain select select 命令自己(没有附带效果),它们从未执行过(除非cte3中引用)。

Use the key word WITH once at the top. If any of your Common Table Expressions (CTE) are recursive (rCTE) you have to add the keyword RECURSIVE at the top once also, even if not all CTEs are recursive:

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

The manual:

If RECURSIVE is specified, it allows a SELECT subquery to
reference itself by name.

Bold emphasis mine. And, even more insightful:

Another effect of RECURSIVE is that WITH queries need not be ordered:
a query can reference another one that is later in the list. (However,
circular references, or mutual recursion, are not implemented.)
Without RECURSIVE, WITH queries can only reference sibling WITH
queries that are earlier in the WITH list.

Bold emphasis mine again. Meaning that the order of WITH clauses is meaningless when the RECURSIVE key word has been used.

BTW, since cte1 and cte2 in the example are not referenced in the outer SELECT and are plain SELECT commands themselves (no collateral effects), they are never executed (unless referenced in cte3).

不羁少年 2025-01-25 14:27:25

是的。您不必重复WITH。您只需使用逗号:

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

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

Yes. You don't repeat the WITH. You just use a comma:

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

And: Only use single quotes for string and date constants. Don't use them for column aliases. They are not allowed for CTE names anyway.

萌化 2025-01-25 14:27:25

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

如果cte2使用cte1cte3使用cte2等,那么CTE之间的依赖链是线性的,并且它是表示为 with,具有 3 个 CTE。相反,如果 cte2 不需要 cte1 并且两者仅在 cte3 中需要,则应考虑将它们嵌套在 cte1 的定义下code>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

As the accepted answer correctly says, the with clause is used only once per a CTE chain. However, for sake of completeness, I would like to add it does not stop you from nesting CTEs.

If cte2 uses cte1, cte3 uses cte2 etc., then the dependency chain between CTEs is linear and it is expressed as with with 3 CTEs. On the contrary, if cte2 doesn't need cte1 and both are needed only in cte3 it should be considered to nest them under definition of cte3 (with cte3 as (with cte1 as (...), cte2 as (...) select...)).

The syntax of CTEs then reflects the dependency tree between CTEs and literally visualizes the scope of partial datasets which can improve readability and prevents scope leakage bugs. Not all db vendors support it but Postgres does.

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
时光无声 2025-01-25 14:27:25

问题原因:在这里,您不必使用多个WITH子句来组合多个CTE。

解决方案:可以在SQL 中使用单个WITH 子句创建多个公共表表达式。两个不同的 CTE 使用单个 WITH 子句创建,并用逗号分隔以创建多个 CTE。

使用单个 With 子句的多个 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 子句创建多个公共表表达式的示例语法。

Problem Reason: Here, you don't have to use multiple WITH clause for combine Multiple CTE.

Solution: It is possible to create the Multiple Common Table Expression's using single WITH clause in SQL. The two different CTE's are created using Single WITH Clause and this is separated by comma to create multiple CTE's.

Sample Multiple CTE's using single

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

This is sample syntax for creating multiple Common Table Expression's with a single With Clause.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文