CTE 和子查询的区别?

Posted

技术标签:

【中文标题】CTE 和子查询的区别?【英文标题】:Difference between CTE and SubQuery? 【发布时间】:2010-10-16 22:59:25 【问题描述】:

来自这个帖子How to use ROW_NUMBER in the following procedure?

答案有两种版本,一种使用sub-query,另一种使用CTE 来解决相同的问题。

那么,与“子查询”相比,使用CTE (Common Table Expression) 有什么优势(因此,查询实际在做什么更可读

使用CTE 而不是sub-select 的唯一优点是我实际上可以命名sub-query。当 CTE 用作简单(非递归)CTE 时,这两个之间还有其他区别吗?

【问题讨论】:

有很好讨论的导数问题:***.com/q/11169550/781695 IMO,任何认为 CTE 可读性较少的人都没有看到一大堆交织在一起的子查询的垃圾堆在使用中令人困惑的锯齿形查询大多数企业数据管理系统。大型的、非平凡的查询通常比子查询更容易在以后或被新人阅读,并且至少在 Postgres 的情况下,在许多情况下神奇地执行好多。 ([出于我尚未理解的原因[(***.com/questions/33731068/…),因为相反的可能性更大。) 【参考方案1】:

在子查询与简单(非递归)CTE 版本中,它们可能非常相似。您必须使用分析器和实际执行计划来发现任何差异,这将取决于您的设置(因此我们无法完整地告诉您答案)。

一般中; CTE 可以递归使用;子查询不能。这使得它们特别适合树结构。

【讨论】:

对不起,我的问题应该更清楚。在使用 CTE LIKE 子查询的上下文中,CTE 和子查询有什么区别? @Marc Gravell:我们可以做的不止这些,因为探查器的行为并不能保证,而 CTE 的行为是(在评估方面)。 不确定该语句对于查看 CTS 和子查询差异的人有多大意义 - A CTE can be used recursively; a sub-query cannot。一个例子会很棒。【参考方案2】:

Common Table Expression(不用于recursive queries 时)的主要优点是封装,无需在您希望使用它的每个地方声明子查询,您只需定义一次即可,但有多个引用。

但是,这意味着它只执行一次(根据previous iterations of this very answer,感谢所有发表评论的人)。如果多次引用,查询肯定有可能被多次执行;查询优化器最终决定如何解释 CTE。

【讨论】:

“将 CTE 视为临时表变量”是否意味着 CTE 存储在磁盘或内存中? 根据定义,您不能在多个查询中使用 CTE 或子查询。我很确定优化器处理子查询的方式与处理 CTE 的方式相同(只评估结果集一次,无论它在 1 个查询中使用了多少次) @AlexCuse:我认为我已经对 CTE 的上下文进行了足够的澄清,但我添加了更多内容以尝试澄清更多内容。 @AlexCuse:也没有暗示 CTE 或子查询可以在多个地方使用。 CTE 和优化器之间的区别在于 CTE 的行为是有保证的,而优化器的行为则不是。 我承认可能存在一些边缘情况,优化器阻塞并且子查询被多次评估,但我没有遇到任何问题。再说一次,我尽可能使用 CTE ;)【参考方案3】:

CTE 对递归最有用:

WITH hier(cnt) AS (
        SELECT  1
        UNION ALL
        SELECT  cnt + 1
        FROM    hier
        WHERE   cnt < @n
        )
SELECT  cnt
FROM    hier

将返回 @n 行(最多 101)。对日历、虚拟行集等很有用。

它们也更具可读性(在我看来)。

除此之外,CTEsubqueries 是相同的。

【讨论】:

在 MSSQL 中,您需要在 WITH 前添加分号 (;),否则会出现错误。应该是;WITH blabla AS ...) @ObinnaNnenanya:仅当它不是批处理中的第一个语句时。无论如何,用分号终止语句是个好主意,即使 SQL Server 在WITHMERGE 和类似版本之前的当前版本中不强制执行它【参考方案4】:

没有提到的一个区别是单个 CTE 可以在联合的多个部分中引用

【讨论】:

【参考方案5】:

除非我遗漏了什么,否则您可以很容易地命名 CTE 和子查询。

我猜主要区别在于可读性(我发现 CTE 更具可读性,因为它在前面而不是在中间定义了您的子查询)。

如果你需要用递归做任何事情,那么用子查询来做这件事会有点麻烦;)

【讨论】:

我不确定是否存在任何非审美差异(尽管我预计在某些情况下执行计划可能会略有不同)。愿意开导我吗? 您可以name CTE,但只能alias 子查询。不同之处在于,您可以重用具有多个别名的 CTE(参见@Michael Petito 在他对 casperOne 的评论中的示例)。我不知道有什么方法可以用子查询来做到这一点。【参考方案6】:

没有人提到的一个重要事实是(至少在 postgres 中),CTE 是优化栅栏:

https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/

也就是说,它们将被视为自己的原子查询,而不是折叠到整个查询计划中。我缺乏提供更好解释的专业知识,但您应该检查您正在使用的 sql 版本的语义;对于高级用户,如果您是控制查询计划器的专家级别,则能够创建优化围栏可以提高性能;然而,在 99% 的情况下,您应该避免试图告诉查询规划器该做什么,因为您认为会更快的可能比它认为会更快的更糟。 :-)

【讨论】:

【参考方案7】:

添加到其他人的答案,如果您有一个相同的子查询多次使用,您可以用一个 CTE 替换所有这些子查询。这使您可以更好地重用您的代码。

【讨论】:

【参考方案8】:

您还需要了解的一件事是,在旧版本的 SQL Server 中(是的,许多人仍然需要支持 SQL Server 2000 数据库),不允许 CTE,然后派生表是您的最佳解决方案。

【讨论】:

【参考方案9】:

提示:(MAXRECURSION n)

您可以限制特定的递归级别的数量 使用MAXRECURSION 提示和介于 032,767OPTION 子句中

例如,您可以尝试:

OPTION 
      (MAXRECURSION 150)

GO

【讨论】:

【参考方案10】:

    通过 CTE,您可以使用递归。

    使用 CTE,您只需编写一次,但您可以在查询中的多个位置引用它。因此,它可以让您避免重复自己,并且还可以使查询更易于阅读和解释(即使在查询只引用一次的情况下)。

    CTE 似乎会向查询优化器提供有关其自身的元数据,因此如果 CTE 在同一查询中被多次引用(例如,如果它连接到自身),则查询优化器可能会使用该元数据来改进整体查询执行计划(这似乎不会发生在子查询中)。

因此,总而言之,如果您想使用递归,或者您认为它会使您的代码更美观、更易于解释,或者您多次使用相同的子查询,请使用 CTE。

【讨论】:

以上是关于CTE 和子查询的区别?的主要内容,如果未能解决你的问题,请参考以下文章

PostgREST 在子查询或 CTE 中使用限制和偏移量

WITH子句和子查询的区别?

CTE、子查询、临时表或表变量之间是不是存在性能差异?

SQL关联查询 直接join 和子查询的区别

mysql8 公用表表达式CTE的使用

内联视图和普通视图的关系以及和子查询的区别