哪些更高性能,CTE或临时表?
哪些更高性能,CTE或临时表?
我会说他们是不同的概念,但没有太多不同的说“粉笔和奶酪”。
-
临时表适合重复使用或对一组数据执行多次处理。
-
一个CTE可以用来缓解或简单地提高可读性。
而且,像一个视图或内联表的重要函数也可以像macros一样对待,在主查询中进行扩展 -
临时表是另一个表,其中有一些围绕着范围的规则
我存储过程中我使用两个(和表variables)
这取决于。
首先
什么是通用expression式?
(非recursion)CTE的处理与其他结构的处理非常相似,这些结构也可以在SQL Server中用作内联表expression式。 派生表,视图和内联表值函数。 请注意,虽然BOL说CTE“可以被认为是临时结果集”,但这是纯粹的逻辑描述。 更多的时候,它本身并没有被本土化。
什么是临时表?
这是存储在tempdb中的数据页上的行的集合。 数据页面可能部分或全部驻留在内存中。 此外,临时表可能被编入索引并具有列统计信息。
testing数据
CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL); INSERT INTO T(B) SELECT TOP (1000000) 0 + CAST(NEWID() AS BINARY(4)) FROM master..spt_values v1, master..spt_values v2;
例1
WITH CTE1 AS ( SELECT A, ABS(B) AS Abs_B, F FROM T ) SELECT * FROM CTE1 WHERE A = 780
在上面的计划中注意到没有提到CTE1。 它只是直接访问基表,并被视为相同
SELECT A, ABS(B) AS Abs_B, F FROM T WHERE A = 780
通过在这里实现CTE到一个中间临时表的重写将是非常有效的。
实现CTE的定义
SELECT A, ABS(B) AS Abs_B, F FROM T
将涉及将大约8GB的数据复制到一个临时表中,然后还有从中select的开销。
例2
WITH CTE2 AS (SELECT *, ROW_NUMBER() OVER (ORDER BY A) AS RN FROM T WHERE B % 100000 = 0) SELECT * FROM CTE2 T1 CROSS APPLY (SELECT TOP (1) * FROM CTE2 T2 WHERE T2.A > T1.A ORDER BY T2.A) CA
上面的例子在我的机器上需要大约4分钟。
1,000,000个随机生成的值中只有15行与谓词匹配,但昂贵的表扫描发生了16次以find这些值。
这将成为实现中间结果的一个很好的select。 等效的临时表重写需要25秒。
INSERT INTO #T SELECT *, ROW_NUMBER() OVER (ORDER BY A) AS RN FROM T WHERE B % 100000 = 0 SELECT * FROM #T T1 CROSS APPLY (SELECT TOP (1) * FROM #T T2 WHERE T2.A > T1.A ORDER BY T2.A) CA
将查询的一部分内容实体化到临时表中有时甚至是有用的,即使它只被评估一次 – 当它允许查询的其余部分被重新编译时,可以利用物化结果的统计信息。 这种方法的一个例子是SQL Cat文章“ 何时分解复杂查询” 。
在某些情况下,SQL Server将使用假脱机caching中间结果,例如CTE,并避免重新评估该子树。 在“连接”项中对此进行了讨论提供了强制实施CTE或派生表的中间实现的提示 。 然而,没有统计数据被创build,即使假定的假脱机行的数量与预计的数量有很大的不同,对于正在进行的执行计划来说也不可能dynamic地适应(至less在当前版本中),自适应查询计划可能成为可能未来)。
CTE有它的用途 – 当CTE中的数据很小时,和recursion表中的情况一样,可读性提高很多。 然而,它的performance当然不比表variables更好,而且当处理非常大的表时,临时表明显胜过CTE。 这是因为你不能在一个CTE上定义索引,并且当你有大量的数据需要与另一个表(CTE就像一个macros)一样。 如果您要连接多个表,每个表中有数百万行logging,则CTE将执行比临时表更差的表。
临时表总是在磁盘上 – 所以只要你的CTE可以保存在内存中,它很可能会更快(就像表variables一样)。
但是,如果CTE(或临时表variables)的数据负载太大,它也会存储在磁盘上,所以没有太大的好处。
一般来说,我喜欢在临时表上使用CTE,因为在使用它之后它就不存在了。 我不需要考虑明确地放弃它或任何东西。
所以最后还是没有明确的答案,但个人而言,我宁愿CTE超过临时表。
CTE不会占用任何物理空间。 这只是我们可以使用连接的结果集。
临时表是暂时的。 我们可以创build索引,像正常表一样约束,我们需要定义所有的variables。
临时表的范围只在会话中。 EX:打开两个SQL查询窗口
create table #temp(empid int,empname varchar) insert into #temp select 101,'xxx' select * from #temp
在第一个窗口中运行此查询,然后在第二个窗口中运行下面的查询,您可以find差异。
select * from #temp
所以我被分配来进行优化的查询是使用SQL Server中的两个CTE编写的。 这是28秒。
我花了两分钟将它们转换为临时表,查询花了3秒钟
我添加了一个索引到它正在join的字段上的临时表,并把它下降到2秒
三分钟的工作,现在它的运行速度提高12倍,全部通过去除CTE。 我个人不会使用他们更难以debugging的CTE。
疯狂的事情是,CTE只用了一次,仍然把一个索引放在他们身上certificate是快了50%。
我已经使用了两个,但是在大规模复杂的过程中总是发现临时表更好地处理和更有条不紊。 CTE有它们的用途,但通常数据很less。
例如,我已经创build了sprocs,它在15秒内返回大量计算结果,但将此代码转换为在CTE中运行,并看到它运行超过8分钟,以实现相同的结果。
我发现CTE的卓越performance的一个用处是我需要将一个相对复杂的查询join到每行几百万行的几个表中。
我使用CTE首先select基于索引列的子集,首先将这些表格分割成几个相关的行,然后将CTEjoin到我的主要查询中。 这成倍地减less了我的查询的运行时间。
虽然CTE的结果没有被caching,表variables可能是一个更好的select,我真的只是想尝试一下,发现适合上述情况。
晚会晚了,但…
我工作的环境受到严格限制,支持一些供应商产品,并提供报告等“增值”服务。 由于政策和合同的限制,我通常不被允许使用单独的表格/数据空间和/或创build永久代码的能力(取决于应用程序会稍微好一些)。
IOW,我通常不能开发一个存储过程或UDF或临时表等等。我几乎必须通过我的应用程序接口(Crystal Reports – 添加/链接表,设置CR中的w / )。 Crystal允许我使用COMMANDS(以及SQLexpression式)。 有些东西通过常规的添加/链接表function是无效的,可以通过定义一个SQL命令来完成。 我通过CTE使用CTE,“远程”获得了非常好的结果。 CTE还帮助报告维护,不要求开发代码,交给DBA编译,encryption,传输,安装,然后需要多级testing。 我可以通过本地界面来做CTE。
使用CTE的不利之处在于,每个报告是分开的。 每个报告都必须保留每个CTE。 在我可以做SP和UDF的地方,我可以开发一些可以被多个报告使用的东西,只需要链接到SP并传递参数,就像在普通表上工作一样。 CR并不擅长处理SQL命令的参数,所以CR / CTE方面的方面可能会有所欠缺。 在这种情况下,我通常会尝试定义CTE以返回足够的数据(但不是所有数据),然后使用CR中的loggingselectfunction对其进行切片和切块。
所以…我的投票是CTE(直到我得到我的数据空间)。
这是一个真正开放的问题,这一切都取决于它如何使用和临时表(表variables或传统表)的types。
传统的临时表将数据存储在临时数据库中,这会降低临时表的速度; 但是表variables不。
我只是testing了这个CTE和非CTE(每个联合实例的查询都被input)都花了31秒。 CTE使代码更可读,尽pipe它从241行减less到130行,这是非常好的。 另一方面,临时表将其削减到132行,并花费五秒钟运行。 不是开玩笑。 所有这些testing都被caching – 查询全部运行多次。
从我在SQL Server的经验中,我发现了CTE胜过Temp表格的场景之一
我需要使用一个DataSet(〜100000)从一个复杂的查询只是一次在我的存储过程。
-
临时表导致SQL的开销,我的过程缓慢执行(因为临时表是存在于tempdb中的实际物化表,而在当前过程的生命期中是存在的)
-
另一方面,对于CTE,CTE只有在执行以下查询之前才存在。 所以,CTE是一个范围有限的方便的内存结构。 CTE默认情况下不使用tempdb。
这是CTE可以真正帮助您简化代码和优于临时表的情况。 我曾经使用过2个CTE,就像
WITH CTE1(ID, Name, Display) AS (SELECT ID,Name,Display from Table1 where <Some Condition>), CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>) SELECT CTE2.ID,CTE2.<col3> FROM CTE2 GO