CTE和SubQuery之间的区别?


143

来自这篇文章如何在以下过程中使用ROW_NUMBER?

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

现在,使用CTE (Common Table Expression)“子查询”而不是“子查询”有什么好处(因此,查询的实际可读性更高)

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


带有很好讨论的衍生问题:stackoverflow.com/q/11169550/781695
用户

7
IMO,任何认为CTE 可读性较差的人,都看不到在大多数企业数据管理系统中使用的大量混乱的锯齿形查询的垃圾堆。大,不平凡的查询通常大大简化后续或新的眼光比子查询,并且至少在Postgres的情况下阅读神奇表演在许多情况下更好。([[出于我尚未理解的原因[(stackoverflow.com/questions/33731068/…,因为相反的可能性似乎更大。)
zxq9

Answers:


102

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

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


1
抱歉,我的问题应该更清楚了。在使用LTE子查询的情况下,CTE和子查询之间有什么区别?
dance2die

2
@Marc Gravell:但是,我们不能做的更多,因为不能保证探查器的行为,而CTE的行为(就评估而言)是可以保证的。
casperOne

1
不确定此语句对查看CTS和子查询差异-的人有意义吗A CTE can be used recursively; a sub-query cannot。一个例子会很棒。
Aniket Thakur

88

Common Table Expression(当不将其用于递归查询时)的主要优点是封装,而不是必须在希望使用它的每个位置声明子查询,您只需定义一次即可,但是有多个引用对此。

但是,这并不能意味着它只执行一次(按照这个非常的答案以前的迭代,感谢所有那些评论)。如果多次引用,该查询肯定有可能多次执行。查询优化器最终决定如何解释CTE。


“将CTE视为临时表变量”是否意味着CTE存储在磁盘或内存中?
dance2die

根据定义,您不能在多个查询中使用CTE或子查询。我非常确定优化器以与处理CTE相同的方式来处理子查询(无论结果在一次查询中使用多少次,都只评估一次结果集)
AlexCuse

@AlexCuse:我想我已经对CTE的上下文进行了足够的澄清,但是我添加了更多内容以尝试澄清更多。
casperOne

@AlexCuse:也没有暗示可以在多个地方使用CTE或子查询。CTE和优化器之间的区别在于,保证了CTE的行为,而优化器却没有保证。
casperOne

我将承认,在某些极端情况下,优化器会阻塞并且对子查询进行多次评估,尽管我没有遇到任何情况。再说一次,我会尽可能使用CTE;)
AlexCuse

15

CTE对于递归最有用:

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

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

它们也更具可读性(我认为)。

除此之外,CTE的和subqueries是相同的。


在MSSQL中,您需要在WITH之前添加分号(;),否则顺序会出现错误。应该是;WITH blabla AS ...)
Obinna Nnenanya

2
@ObinnaNnenanya:仅当它不是批处理中的第一条语句时。无论如何,用分号终止语句都是一个好主意,即使SQL Server不在当前版本(而非以前WITHMERGE和类似版本上强制执行它
Quassnoi

10

未提及的一个不同之处是,可以在工会的多个部分中引用单个CTE


8

除非我缺少任何内容,否则您可以轻松命名CTE和子查询。

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

而且,如果您需要对递归进行任何操作,那么在使用子查询进行操作时会遇到麻烦;)


1
我不知道有任何非审美的差异(尽管我希望,在某些情况下,可能在执行计划的细微差别)。想启发我吗?
AlexCuse 2010年

2
您可以命名 CTE,但只能使用别名子查询。不同之处在于,您可以重复使用具有多个别名的CTE(请参阅@Michael Petito在casperOne的评论中的示例)。我不知道用子查询来做任何事情。
kmote

7

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

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

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


6

除其他答案外,如果您一次使用同一个子查询多次,则可以用一个CTE替换所有这些子查询。这使您可以更好地重用代码。


4

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


2

提示:(MAXRECURSION n)

您可以通过使用MAXRECURSION提示和子句中介032,767之间的值来限制特定语句允许的递归级别数OPTION

例如,您可以尝试:

OPTION 
      (MAXRECURSION 150)

GO
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.