4

因此,根据我所看到的WITH使用方式以及MSDN 上的文档

指定一个临时命名结果集,称为公用表表达式 (CTE)。

似乎WITH是表的替代品#TEMP。那是对的吗?

4

3 回答 3

14

不。CTE——由——引入的——WITH不要替换临时表,尽管在某些情况下,它们可以用于过去可能使用临时表的地方。

WITH实际上只不过是一个派生表,不同之处在于它是在查询之前引入而不是内联,并被赋予一个别名,然后可以在整个查询中多次将其用作表。

派生表是一个完整的查询,在括号内,就像它是一个真实的表一样使用。视图和表值函数也被视为派生表,但我们关注的是内联定义的类型。这是一个例子:

SELECT
   C.Name,
   S.SalesTotal
FROM
   dbo.Customer C
   INNER JOIN (
      SELECT
         O.CustomerID,
         SalesTotal = Sum(OrderTotal)
      FROM
         dbo.CustomerOrder O
      GROUP BY
         O.CustomerID
   ) S
      ON C.CustomerID = S.CustomerID;

我们有一个完整的查询,它返回自己的行集(GROUP BY查询)。通过将它放在括号内并为其分配一个别名S,我们现在可以像使用表格一样使用它。我们可以将更多的表加入到这个表中。但是,我们只加入了这个表一次。

要将其转换为 CTE,我们进行了非常简单的更改:

WITH SalesTotals AS (
   SELECT
      O.CustomerID,
      SalesTotal = Sum(OrderTotal)
   FROM
      dbo.CustomerOrder O
   GROUP BY
      O.CustomerID
)
SELECT
   C.Name,
   S.SalesTotal
FROM
   dbo.Customer C
   INNER JOIN SalesTotals S
      ON C.CustomerID = S.CustomerID
   -- and for an example of using the CTE twice:
   INNER JOIN (
      SELECT Avg(SalesTotal)
      FROM SalesTotals
   ) A (AverageSalesTotal)
      ON S.SalesTotal >= A.AverageSalesTotal;

现在,临时表是完全不同的动物。它与 CTE 或派生表有非常重要的区别:

  • 临时表在许多查询中持续存在(在客户端连接的生命周期内,或直到显式删除),但 CTE 仅针对一个查询“存在”。
  • CTE 虽然在逻辑上是一个“单个”表,但如果在查询中多次使用,它的数据可能会生成多次。临时表的数据将被简单地读取为任何其他“真实”表。在上面的示例中,Avg(SalesTotal)在至少 2012 年之前的 SQL Server 版本中,计算将涉及SalesTotals第二次执行聚合的完全独立的操作。虽然引擎可以实现 CTE 的结果,但到目前为止 SQL Server 还没有做到这一点。值得注意的是,Oracle 等其他 DBMS 可能会实现 CTE 的结果。无论如何,您应该知道这种双重查询可能(当然!)严重的性能影响。
  • 临时表会自动为其生成列统计信息,这可以帮助查询优化器选择更好的执行计划。CTE 的“最终”行集没有统计信息——使用基础表的统计信息。
  • 临时表可以增量添加,也可以通过多个或重复语句从中删除行。它可以更新。
  • 可以修改临时表以添加或删除列或更改数据类型。
  • 临时表可以具有聚集和非聚集索引和约束。
  • 您不能以任何方式在用户定义的函数中使用临时表。
  • CTE 虽然看起来在逻辑上隔离了查询的各个部分,但并没有做这样的事情。CTE 是谓词下推、消除的完美候选者,如果确定它们不影响最终行集(或它们的某些表或连接已消除),或者它们可能会受到意外的表达式评估顺序的影响。例如,在 CTE 中,您可能只返回文本列中的数字字符串,并且在外部查询中尝试将这些字符串转换为数字数据类型,但令您惊讶的是,您在尝试转换非数字字符串时遇到错误为数值数据类型。这是因为优化器可以自由地以任何它喜欢的方式重新组织您的查询,并且可以在过滤包含数字的字符串之前将其转换为数字。一个临时表,同时需要两条语句(一条用于插入数据,

最后,CTE 可以做一些临时表不能做的事情:它可以是递归的。在 Oracle 中,这是通过 表示的CONNECT BY,而在 SQL Server 中,它是通过UNION ALL SELECTCTE 内部的 a 来完成的,该 CTE 允许引用 CTE 自己的别名。

小心 CTE——它们是一个很好的抽象,但仅此而已,你可能会遇到严重的麻烦。生成一百万行可以使用递归 CTE 一次一行来完成,但这是最糟糕的方法,可能是一百倍或更多。

SQL Server 2005 和更高版本中还有另一种特殊的临时表,称为“表变量”,它非常类似于临时表(并且在 tempdb 中保持完全相同),但有一些值得注意的例外:

  • 它只持续批处理的持续时间,而不是连接
  • 您可以在用户定义的函数中使用表变量。某些类型的 UDF 需要一个。
  • 它只能声明内联约束(例如主键或唯一性),虽然它可以更新/插入/删除行,但在声明后不能以任何方式修改其架构,因此不能添加/删除列,更改数据类型,或添加索引。
  • 它不收集统计信息。
  • 它可以在 SQL Server 2008 及更高版本中作为参数(表值参数)传递。
于 2013-02-12T17:28:10.193 回答
1

今天,SQL 优化器在选择出色的执行计划方面做得更好,但是当连接超过 10 个表时,尤其是对于一些大型表和视图并且需要使用多个过滤器时,它通常不会以最佳方式执行。我仍然发现没有什么比使用#TEMP 表和在将它们连接在一起之前将查询分解成更小的子集更快的了。注意:我很少发现向#TEMP 表添加索引可以提高性能。

于 2013-02-12T21:16:14.317 回答
0

不,这是不正确的。它们都是独立的功能,每个都有各自的用途。

例如,CTE 适用于少量数据,而临时表通常更适合较大的数据集。临时表可以被索引并提高它们的性能,而 CTE 不能。

我会花一点时间阅读 MSDN 文档并查看您将使用其中一个或另一个的特定实例

于 2013-02-12T17:31:43.307 回答