【问题标题】:Which are more performant, CTE or temporary tables?哪个性能更高,CTE 或临时表?
【发布时间】:2010-10-15 23:36:19
【问题描述】:

CTETemporary Tables 哪个性能更高?

【问题讨论】:

标签: sql-server performance sql-server-2008 temp-tables common-table-expression


【解决方案1】:

这是一个非常开放式的问题,这完全取决于它的使用方式和临时表的类型(表变量或传统表)。

传统的临时表将数据存储在临时数据库中,这会降低临时表的速度;但是表变量没有。

【讨论】:

    【解决方案2】:

    临时表总是在磁盘上——所以只要你的 CTE 可以保存在内存中,它很可能会更快(也像一个表变量)。

    但话又说回来,如果 CTE(或临时表变量)的数据负载太大,它也会存储在磁盘上,因此没有什么大好处。

    一般来说,我更喜欢 CTE 而不是临时表,因为它在我使用后就消失了。我不需要考虑明确删除它或其他任何事情。

    所以,最终没有明确的答案,但就个人而言,我更喜欢 CTE 而不是临时表。

    【讨论】:

    • 在 SQLite 和 PostgreSQL 的情况下,临时表 会自动删除(通常在会话结束时)。不过我不知道其他 DBMS。
    • CTE 就像一个临时视图。不存储 AFAIK 数据,因此没有任何东西可以保存在内存中或存储在磁盘上。重要提示,每次使用 CTE 时,查询都会再次运行。
    • 就我个人而言,我从未见过 CTE 在速度方面比 Temp 表更好。使用临时表进行调试会容易得多
    【解决方案3】:

    我会说它们是不同的概念,但不能说“粉笔和奶酪”。

    • 临时表适合重复使用或对一组数据执行多次处理。

    • CTE 可用于递归或简单地提高可读性。
      而且,像视图或内联表值函数也可以像宏一样在主查询中展开

    • 临时表是另一个表,在范围内有一些规则

    我已将 procs 存储在我同时使用的地方(以及表变量)

    【讨论】:

    • 临时表还允许索引甚至有时是必需的统计信息,而 CTE 则不允许。
    • 我认为这个答案并没有充分强调 CTE 会导致糟糕的性能这一事实。我通常在 dba.stackexchange 上参考这个answer。如果我正在查找cte vs temporary tables,您的问题在我的搜索引擎中排在第二位,所以恕我直言,这个答案需要突出 CTE 的缺点。 TL;链接答案的 DR:CTE 永远不应该用于性能。。我同意这句话,因为我经历过 CTE 的缺点。
    • @TT。有趣的。我发现 CTE 的表现要好得多
    【解决方案4】:

    CTE 有其用途 - 当 CTE 中的数据很小并且可读性得到很大改进时,例如递归表中的情况。但是,它的性能肯定不比表变量好,当处理非常大的表时,临时表的性能明显优于 CTE。这是因为您无法在 CTE 上定义索引,并且当您有大量数据需要与另一个表连接时(CTE 就像一个宏)。如果您要连接多个表,每个表都有数百万行记录,CTE 的性能会比临时表差很多。

    【讨论】:

    • 我从自己的经验中看到了这一点。 CTE 的执行速度明显变慢。
    • CTE 的执行速度也较慢,因为结果未缓存。因此,每次您使用 CTE 时,它都会重新运行查询、计划和所有内容。
    • 并且数据库引擎可以选择重新运行查询,不仅是每个引用,而且对于消费者查询的每一 ,作为一个相关的子查询......你必须如果不需要,请始终注意这一点。
    • 临时表存储在 SQL Server 上的 tempdb 中,这是磁盘,但具有被索引的好处,并且 SQL 优化器在这种情况下可以很好地处理选择查询。不确定 CTE 存储在哪个数据库或磁盘区域(当它超过内存大小并排队等待 IO 分页时),但它从未针对大量数据进行优化。我有时会使用编译器选项(重新编译)以使其更快
    【解决方案5】:

    聚会迟到了,但是……

    我工作的环境受到高度限制,支持一些供应商产品并提供报告等“增值”服务。由于政策和合同的限制,我通常不被允许拥有单独的表/数据空间和/或创建永久代码的能力[它会变得更好,取决于应用程序]。

    IOW,我不能通常开发存储过程或 UDF 或临时表等。我几乎必须通过我的应用程序界面(Crystal Reports - 添加/链接表,设置来自 w/in CR 等的 where 子句。一个小小的优点是 Crystal 允许我使用命令(以及 SQL 表达式)。一些通过常规添加/链接表功能效率不高的事情可以通过定义 SQL 命令来完成。我通过它使用 CTE,并“远程”获得了非常好的结果。 CTE 还有助于报告维护,不需要开发代码、交给 DBA 编译、加密、传输、安装,然后需要多级测试。我可以通过本地界面进行 CTE。

    使用带 CR 的 CTE 的缺点是,每个报告都是独立的。必须为每个报告维护每个 CTE。在我可以做 SP 和 UDF 的地方,我可以开发一些可供多个报告使用的东西,只需要链接到 SP 并传递参数,就好像您在处理常规表格一样。 CR 并不擅长将参数处理到 SQL 命令中,因此可能缺少 CR/CTE 方面的这方面。在这些情况下,我通常会尝试定义 CTE 以返回足够的数据(但不是所有数据),然后使用 CR 中的记录选择功能对其进行切片和切块。

    所以...我投票支持 CTE(直到我得到我的数据空间)。

    【讨论】:

      【解决方案6】:

      CTE 不会占用任何物理空间。它只是一个我们可以使用 join 的结果集。

      临时表是临时的。我们可以像普通表一样创建索引和约束,为此我们需要定义所有变量。

      临时表的范围仅在会话内。 前任: 打开两个SQL查询窗口

      create table #temp(empid int,empname varchar)
      insert into #temp 
      select 101,'xxx'
      
      select * from #temp
      

      在第一个窗口中运行此查询 然后在第二个窗口中运行以下查询,您可以找到不同之处。

      select * from #temp
      

      【讨论】:

      • >> "它只是一个我们可以使用 join 的结果集。" -> 这不准确。 CTE 不是“结果集”,而是内联代码。 SQL Server 查询引擎将 CTE 代码解析为查询文本的一部分,并据此构建执行计划。 CTE 内联的想法是使用 CTE 的一大优势,因为它允许服务器创建“组合执行计划”
      【解决方案7】:

      我发现 CTE 的出色性能的一个用途是我需要将一个相对复杂的查询加入到几个表中,每个表都有几百万行。

      我使用 CTE 首先选择基于索引列的子集,首先将这些表减少到每个相关的几千行,然后将 CTE 加入到我的主查询中。这成倍地减少了我的查询的运行时间。

      虽然 CTE 的结果没有被缓存,并且表变量可能是更好的选择,但我真的只是想尝试一下,发现它们适合上述场景。

      【讨论】:

      • 另外,我认为因为我只在连接中使用 CTE,所以我只在查询中真正执行过一次 CTE,因此缓存结果在这方面并不是一个大问题
      【解决方案8】:

      我刚刚对此进行了测试——CTE 和非 CTE(为每个联合实例输入查询)都花费了大约 31 秒。 CTE 使代码更具可读性——将其从 241 行减少到 130 行,这非常好。另一方面,临时表将其减少到 132 行,并用了五秒钟来运行。可不是闹着玩的。所有这些测试都被缓存了——查询之前都运行了多次。

      【讨论】:

        【解决方案9】:

        我都使用过这两种方法,但在大量复杂的过程中,我总是发现临时表更易于使用且更有条理。 CTE 有其用途,但通常用于小数据。

        例如,我创建了 15 秒内返回大型计算结果的存储过程,但将此代码转换为在 CTE 中运行,并且看到它运行超过 8 分钟才能达到相同的结果。

        【讨论】:

        • 是的,我喜欢这个评论。似乎有一种奇怪的范式,如果我可以用一行代码而不是两行代码编写一些东西,我应该这样做。我现在正在调试一个嵌套了 13 个 CTE 的东西,这些 CTE 被称为 data1-data13。完全精神错乱。
        【解决方案10】:

        视情况而定。

        首先

        什么是公用表表达式?

        A(非递归)CTE 的处理方式与其他可用作 SQL Server 中的内联表表达式的构造非常相似。派生表、视图和内联表值函数。请注意,虽然 BOL 说 CTE“可以被认为是临时结果集”,但这是一个纯粹的逻辑描述。通常情况下,它本身并没有具体化。

        什么是临时表?

        这是存储在 tempdb 数据页上的行集合。数据页可以部分或全部驻留在内存中。此外,临时表可能会被索引并具有列统计信息。

        测试数据

        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 的​​数据复制到临时表中,然后仍然存在从中进行选择的开销。

        示例 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 次以找到这些值。

        这将是实现中间结果的理想选择。等效的临时表重写需要 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 文章When To Break Down Complex Queries 中提供了这种方法的一个示例。

        在某些情况下,SQL Server 会使用假脱机来缓存中间结果,例如的 CTE,并避免重新评估该子树。这在(迁移的)连接项Provide a hint to force intermediate materialization of CTEs or derived tables 中进行了讨论。但是,没有对此创建统计信息,即使假脱机行数与估计的有很大不同,正在进行的执行计划也无法动态适应响应(至少在当前版本中。自适应查询计划可能会在未来)。

        【讨论】:

        • 这是回答实际问题的唯一答案(即询问哪个性能更好,而不是有什么区别或您最喜欢哪个),并且它正确回答了这个问题:“取决于”是正确的答案。这也是唯一有支持数据来解释的答案,其他几个(投票数很高)明确声称一个比另一个更好,没有参考或证据......要清楚,所有这些答案也是 错误。因为“这取决于”
        • 这也是一个写得很好,参考很好的答案。真的是一流的。
        • 我喜欢强调这部分,我发现它是真实的 将查询的一部分中间物化到临时表中有时会很有用,即使它只被评估一次
        【解决方案11】:

        根据我在 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
        

        【讨论】:

        • 您的回答似乎很笼统......您如何衡量“CTE 优于 Temp 表”?你有时间测量吗?我认为您应该编辑您的答案并添加更多详细信息。
        • 是的,我有时间测量和执行计划来支持我的陈述。
        • 由于权限有限,无法添加执行计划的img。解决后将更新详细信息
        【解决方案12】:

        所以我被分配优化的查询是用 SQL Server 中的两个 CTE 编写的。耗时 28 秒。

        我花了两分钟将它们转换为临时表,查询耗时 3 秒

        我在正在加入的字段的临时表中添加了一个索引,并将其缩短到 2 秒

        三分钟的工作,现在通过移除 CTE,它的运行速度提高了 12 倍。我个人不会使用 CTE,因为它们也更难调试。

        疯狂的是,CTE 都只使用了一次,并且仍然在它们上放置索引,事实证明速度提高了 50%。

        【讨论】:

          猜你喜欢
          • 2011-01-20
          • 2019-11-21
          • 2011-07-15
          • 1970-01-01
          • 2022-10-05
          • 1970-01-01
          • 1970-01-01
          • 2021-07-26
          • 1970-01-01
          相关资源
          最近更新 更多