是否替换#TEMP表?

Mik*_*oud 2 t-sql sql-server common-table-expression sql-server-2008

所以基于我看过的方式WITH,以及MSDN上的文档:

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

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

Eri*_*ikE 11

CTE - 由WITH--don't 引入 - 替换临时表,尽管在某些情况下,它们可以用于过去可能使用临时表的地方.

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;
Run Code Online (Sandbox Code Playgroud)

我们有一个完整的查询,返回自己的行集(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;
Run Code Online (Sandbox Code Playgroud)

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

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

最后,CTE可以做临时表不能做的事情:它可以是递归的.在Oracle中,这表示通过CONNECT BY,并且在SQL Server中,它是UNION ALL SELECT在CTE内部完成的,允许引用CTE自己的别名.

小心CTE - 它们是一个很好的抽象,但只不过是这样,你可能会遇到严重问题.生成一百万行可以一次一行地使用递归CTE来完成,但它可能是最差的一百次或多次.

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

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