SQL:是否有任何理由使用子查询而不是 CTE?
SQL: is there ever any reason to use a subquery instead of a CTE?
我见过许多 SQL 的示例,其中包含复杂的嵌套子查询(以及子子查询和子子子查询以及...)。是否有任何理由编写这样的复杂查询而不是使用 WITH 和 CTE,就像在编程语言中使用变量来分解复杂表达式一样?
特别是有性能优势吗?
除非您的查询计划告诉您子查询性能优于 CTE,否则我会使用 CTE
而不是子查询。
In particular, is there a performance advantage?
子查询与简单(非递归)CTE 版本,它们可能非常相似。您将不得不使用探查器和实际执行计划来发现任何差异。
有一些原因我会使用 CTE
- 一般来说,
CTE
可以递归使用,子查询做不到,可以帮助你做一个日历table或者特别适合树结构
CTE
将更易于维护和阅读(@Lukasz Szozda 评论),因为您可以将复杂的查询分解为几个 CTE 并为它们命名,这将非常舒适 table 在主查询中写入时。
您可以仅使用 FROM
子句中的子查询和常规联接编写的任何查询都可以使用具有直接替换的 CTE。
需要子查询:
- 相关子查询(通常不在
FROM
子句中)。
- 横向联接(在
FROM
子句中支持 LATERAL
或 APPLY
关键字的数据库中)。
- 标量子查询。
有时,可以重写查询以避免这些构造。
FROM
子句中的子查询(横向连接除外)可以使用 CTE 编写。
为什么使用子查询而不使用 CTE?最重要的原因是 CTE 是后来添加到 SQL 语言中的。除了递归 CTE,它们并不是真正需要的。当一个子查询被多次引用时,它们真的很方便,但有人可能会争辩说视图服务于相同的目的。
如评论中所述,CTE 和子查询的优化方式可能有所不同。这可能是性能提升或损失,具体取决于查询和基础索引等。
不考虑性能:
CTE 作为 sql 代码更具可读性,这意味着更易于维护和调试。
子查询(在 FROM 子句中)只要数量少且简单就很好,因此转换为 CTE 实际上会使它更难阅读。
还有视图选项,主要防止 sql 代码重复。
考虑到性能:
CTE 可能会搞砸它们变得越复杂。如果是这样,他们变得太冒险了,无法信任一些 teaks 和变化,并可能导致更积极的性能方法,如将所有 CTE 转换为临时工 (#)。
在大多数情况下,子查询的行为与视图一样好,但比 CTE 好不了多少。仍然变得过于复杂可能会影响性能并使性能优化变得困难。最终有人可能需要调整它们,甚至将较重的部分提取出来以减轻主要 select 的温度。
只要视图由普通表格和简单视图组成,视图在增加复杂性方面稍微好一些,它们具有优雅的 SQL 并且可能的过滤器在视图的连接中尽可能链接。仍然加入两个复杂视图将使您遇到与复杂 CTE 或子查询相同的情况。
我见过许多 SQL 的示例,其中包含复杂的嵌套子查询(以及子子查询和子子子查询以及...)。是否有任何理由编写这样的复杂查询而不是使用 WITH 和 CTE,就像在编程语言中使用变量来分解复杂表达式一样?
特别是有性能优势吗?
除非您的查询计划告诉您子查询性能优于 CTE,否则我会使用 CTE
而不是子查询。
In particular, is there a performance advantage?
子查询与简单(非递归)CTE 版本,它们可能非常相似。您将不得不使用探查器和实际执行计划来发现任何差异。
有一些原因我会使用 CTE
- 一般来说,
CTE
可以递归使用,子查询做不到,可以帮助你做一个日历table或者特别适合树结构 CTE
将更易于维护和阅读(@Lukasz Szozda 评论),因为您可以将复杂的查询分解为几个 CTE 并为它们命名,这将非常舒适 table 在主查询中写入时。
您可以仅使用 FROM
子句中的子查询和常规联接编写的任何查询都可以使用具有直接替换的 CTE。
需要子查询:
- 相关子查询(通常不在
FROM
子句中)。 - 横向联接(在
FROM
子句中支持LATERAL
或APPLY
关键字的数据库中)。 - 标量子查询。
有时,可以重写查询以避免这些构造。
FROM
子句中的子查询(横向连接除外)可以使用 CTE 编写。
为什么使用子查询而不使用 CTE?最重要的原因是 CTE 是后来添加到 SQL 语言中的。除了递归 CTE,它们并不是真正需要的。当一个子查询被多次引用时,它们真的很方便,但有人可能会争辩说视图服务于相同的目的。
如评论中所述,CTE 和子查询的优化方式可能有所不同。这可能是性能提升或损失,具体取决于查询和基础索引等。
不考虑性能: CTE 作为 sql 代码更具可读性,这意味着更易于维护和调试。 子查询(在 FROM 子句中)只要数量少且简单就很好,因此转换为 CTE 实际上会使它更难阅读。 还有视图选项,主要防止 sql 代码重复。 考虑到性能: CTE 可能会搞砸它们变得越复杂。如果是这样,他们变得太冒险了,无法信任一些 teaks 和变化,并可能导致更积极的性能方法,如将所有 CTE 转换为临时工 (#)。 在大多数情况下,子查询的行为与视图一样好,但比 CTE 好不了多少。仍然变得过于复杂可能会影响性能并使性能优化变得困难。最终有人可能需要调整它们,甚至将较重的部分提取出来以减轻主要 select 的温度。 只要视图由普通表格和简单视图组成,视图在增加复杂性方面稍微好一些,它们具有优雅的 SQL 并且可能的过滤器在视图的连接中尽可能链接。仍然加入两个复杂视图将使您遇到与复杂 CTE 或子查询相同的情况。