【问题标题】:Multiple CTE in single query单个查询中的多个 CTE
【发布时间】:2016-05-16 20:25:15
【问题描述】:

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

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

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

【问题讨论】:

    标签: sql postgresql common-table-expression arel recursive-cte


    【解决方案1】:

    在顶部使用关键字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 ...
    

    The manual:

    如果指定了RECURSIVE,它允许 SELECT 子查询 按名称引用自身。

    我的大胆强调。而且,更有洞察力:

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

    再次大胆强调我的。这意味着当使用了RECURSIVE 关键字时,WITH 子句的顺序是没有意义的

    顺便说一句,由于示例中的 cte1cte2 没有在外部 SELECT 中引用,并且它们本身是普通的 SELECT 命令(没有附带影响),它们永远不会被执行(除非在 cte3 中引用)。

    【讨论】:

    • Erwin Trendsetter 奠定了 CTE 知识。 +1 - 感谢您的洞察力!
    【解决方案2】:

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

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

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

    【讨论】:

    • 但是如果我需要一个递归 CTE 和 2 个非递归?
    • 但是 cte1、cte2、cte3 不能互相引用,对吗?
    • @thadk 。 . .它们可以,没有限制,除了在使用之前需要定义 CTE。
    • 谢谢!当我试图在 cte2 中引用 cte1 时,我无法让它工作,但我可能打错了或什么的。
    【解决方案3】:

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

    解决方案:可以在 SQL 中使用单个 WITH 子句创建多个公用表表达式。这两个不同的 CTE 是使用 Single WITH 子句创建的,并用逗号分隔以创建多个 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 子句创建多个公用表表达式的示例语法。

    【讨论】:

      【解决方案4】:

      正如公认的正确答案所说,with 子句在每个 CTE 链中仅使用一次。但是,为了完整起见,我想补充一下它不会阻止您嵌套 CTE

      如果cte2使用cte1cte3使用cte2等,则CTE之间的依赖链是线性的,表示为with有3个CTE。相反,如果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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多