是否替换#TEMP表?

时间:2013-02-12 17:26:10

标签: sql-server sql-server-2008 tsql common-table-expression

基于我看过WITH使用的方式以及documentation at MSDN

  

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

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

3 个答案:

答案 0 :(得分:11)

没有。 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,则逻辑上是“单个”表,可能会多次生成其数据。临时表的数据将被简单地读取为任何其他“真实”表。在上面的示例中,至少在2012年的SQL Server版本中,Avg(SalesTotal)计算将涉及第二次执行SalesTotals聚合的完全独立的操作。虽然引擎有可能实现CTE的结果,但到目前为止SQL Server还没有这样做。值得注意的是,其他DBMS(如Oracle)可能会实现CTE的结果。无论如何,你应该知道这种双重查询可能会(当然!)严重影响性能。
  • 临时表具有为其自动生成的列统计信息,这可以帮助查询优化器选择更好的执行计划。 CTE的“最终”行集没有统计信息 - 使用基础表的统计信息。
  • 可以逐步添加临时表,或者通过多个或重复的语句从中删除行。它可以更新。
  • 可以修改临时表以添加或删除列或更改数据类型。
  • 临时表可以具有聚簇索引和非聚簇索引和约束。
  • 您不能以任何方式在用户定义的函数中使用临时表。
  • CTE虽然似乎在逻辑上隔离了查询的某些部分,却没有这样做。如果确定它们不影响最终行集(或者它们的某些表或联接被消除),则CTE是谓词下推,消除的完美候选者,或者它们可能受到意外的表达式评估顺序的影响。例如,在CTE中,您可能只返回文本列中的数字字符串,并在外部查询中尝试将这些字符串转换为数字数据类型,但令您惊讶的是,您会收到有关尝试转换非数字字符串的错误到数字数据类型。这是因为优化器可以以任何方式自由地重新组织您的查询,并且可以在包含数字的字符串的过滤器之前转换为数字。临时表虽然需要两个语句(一个用于插入数据,另一个用于连接到该数据)但不会出现此问题,因为查询是不同的,并且数据在使用之前已按预期“物化”。

最后,CTE可以执行临时表不能执行的操作:它可以是递归的。在Oracle中,这通过CONNECT BY表示,在SQL Server中,它在CTE内部使用UNION ALL SELECT来完成,允许引用CTE自己的别名。

小心CTE - 它们是一个伟大的抽象,但只不过是,你可能会遇到严重的问题。生成一百万行可以使用递归CTE一次一行完成,但它可能是最差的一百次或更多。

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

  • 它仅持续批次的持续时间,而不是连接
  • 您可以在用户定义的函数中使用表变量。某些类型的UDF需要一个。
  • 它只能声明内联约束(例如主键或唯一性),虽然它可以更新/插入/删除行,但是在声明之后无法以任何方式修改其模式,因此不会添加/删除列,从而更改数据类型或添加索引。
  • 它不收集统计数据。
  • 它可以作为参数(表值参数)在SQL Server 2008及更高版本中传递。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

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

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