why*_*heq 200 sql t-sql sql-server subquery common-table-expression
在这个优秀的SO问题中,讨论了CTE和之间的sub-queries差异.
我想具体问一下:
在什么情况下,以下各项更有效/更快?
传统上,我temp tables在开发中使用了很多stored procedures- 因为它们看起来比许多交织在一起的子查询更具可读性.
Non-recursive CTEs非常好地封装数据集,并且非常易读,但是在某些情况下可以说它们总能表现得更好吗?或者是否必须总是摆弄不同的选项才能找到最有效的解决方案?
编辑
我最近被告知,就效率而言,临时表是一个很好的首选,因为它们具有相关的直方图即统计数据.
Gor*_*off 222
SQL是一种声明性语言,而不是一种过程语言.也就是说,您构造一个SQL语句来描述所需的结果.您没有告诉SQL引擎如何完成工作.
作为一般规则,让SQL引擎和SQL优化器找到最佳查询计划是个好主意.开发SQL引擎需要花费很多人一年的时间,所以让工程师做他们知道怎么做的事情.
当然,有些情况下查询计划不是最佳的.然后,您希望使用查询提示,重组查询,更新统计信息,使用临时表,添加索引等,以获得更好的性能.
至于你的问题.理论上,CTE和子查询的性能应该相同,因为它们都向查询优化器提供相同的信息.一个不同之处在于,使用一次以上的CTE可以很容易地识别和计算一次.然后可以多次存储和读取结果.不幸的是,SQL Server似乎没有利用这种基本的优化方法(你可以称之为常见的子查询消除).
临时表是另一回事,因为您提供了有关如何运行查询的更多指导.一个主要区别是优化器可以使用临时表中的统计信息来建立其查询计划.这可以带来性能提升.此外,如果您有一个多次使用的复杂CTE(子查询),那么将其存储在临时表中通常会提高性能.查询仅执行一次.
您的问题的答案是您需要四处寻找您期望的性能,特别是对于定期运行的复杂查询.在理想的世界中,查询优化器将找到完美的执行路径.虽然经常这样做,但您可以找到一种获得更好性能的方法.
Aar*_*and 73
没有规则.我发现CTE更具可读性,除非它们出现性能问题,否则使用它们,在这种情况下,我会调查实际问题,而不是猜测CTE是问题,并尝试使用不同的方法重新编写它.问题通常比我选择以声明方式陈述我对查询的意图的方式更多.
当然,您可以解开CTE或删除子查询并将其替换为#temp表并减少持续时间.这可能是由于各种各样的事情,例如陈旧的统计数据,甚至无法获得准确的统计数据(例如加入表值函数),并行性,甚至由于查询的复杂性而无法生成最优计划(在这种情况下,打破它可能会给优化者一个战斗机会).但是也存在这样的情况:创建#temp表所涉及的I/O可能超过可能使得使用CTE的特定计划形状不那么有吸引力的其他性能方面.
老实说,有太多的变量可以为你的问题提供"正确"的答案.没有可预测的方法可以知道查询何时可能倾向于支持一种方法或另一种方法 - 只要知道理论上CTE或单个子查询的相同语义应该执行完全相同的方法.如果你提出一些不正确的情况,我认为你的问题会更有价值 - 可能是你在优化器中发现了一个限制(或发现了一个已知的限制),或者你的查询可能在语义上不等同或者那个包含阻碍优化的元素.
因此,我建议以对您来说最自然的方式编写查询,并且只有在您发现优化程序遇到的实际性能问题时才会出现偏差.我个人将他们排在CTE,然后是子查询,#temp表是最后的手段.
pap*_*zzo 17
#temp是materalized而CTE不是.
CTE只是语法,所以理论上它只是一个子查询.它被执行了.#temp已实现.因此,在#temp中,执行多次的连接中的昂贵CTE可能更好.另一方面,如果它是一个简单的评估,但没有执行,但有几次不值得#temp的开销.
SO上的一些人不喜欢表变量,但我喜欢它们,因为它们是物化的,比#temp更快.有时候,查询优化器使用#temp与表变量相比做得更好.
在#temp或table变量上创建PK的能力为查询优化器提供了比CTE更多的信息(因为您无法在CTE上声明PK).
小智 11
我认为只需要2件事情就可以使用#Temp表而不是CTE更好:
您不能将主键放在CTE上,因此CTE访问的数据必须遍历CTE表中的每个索引,而不是仅访问临时表上的PK或索引.
因为您无法向CTE添加约束,索引和主键,所以它们更容易出现错误和错误的数据.
- 昨天
下面是一个示例,其中#table约束可以防止错误数据,这在CTE中并非如此
DECLARE @BadData TABLE (
ThisID int
, ThatID int );
INSERT INTO @BadData
( ThisID
, ThatID
)
VALUES
( 1, 1 ),
( 1, 2 ),
( 2, 2 ),
( 1, 1 );
IF OBJECT_ID('tempdb..#This') IS NOT NULL
DROP TABLE #This;
CREATE TABLE #This (
ThisID int NOT NULL
, ThatID int NOT NULL
UNIQUE(ThisID, ThatID) );
INSERT INTO #This
SELECT * FROM @BadData;
WITH This_CTE
AS (SELECT *
FROM @BadData)
SELECT *
FROM This_CTE;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
116110 次 |
| 最近记录: |