Sql 为什么插入和连接临时表更快?
我有一个查询,看起来像Sql 为什么插入和连接临时表更快?,sql,sql-server,sql-server-2005,tsql,Sql,Sql Server,Sql Server 2005,Tsql,我有一个查询,看起来像 SELECT P.Column1, P.Column2, P.Column3, ... ( SELECT A.ColumnX, A.ColumnY, ... FROM dbo.TableReturningFunc1(@StaticParam1, @StaticParam2) AS A WHERE A.Key = P.Key FOR XML AUTO, TYPE ),
SELECT
P.Column1,
P.Column2,
P.Column3,
...
(
SELECT
A.ColumnX,
A.ColumnY,
...
FROM
dbo.TableReturningFunc1(@StaticParam1, @StaticParam2) AS A
WHERE
A.Key = P.Key
FOR XML AUTO, TYPE
),
(
SELECT
B.ColumnX,
B.ColumnY,
...
FROM
dbo.TableReturningFunc2(@StaticParam1, @StaticParam2) AS B
WHERE
B.Key = P.Key
FOR XML AUTO, TYPE
)
FROM
(
<joined tables here>
) AS P
FOR XML AUTO,ROOT('ROOT')
具有约4秒的性能
这没有多大意义,因为在默认情况下,插入临时表然后进行连接的成本似乎更高。我倾向于认为SQL对子查询执行了错误的联接类型,但可能我错过了,无法指定用于相关子查询的联接类型
有没有办法不通过索引和/或提示使用temp tables/@table变量来实现这一点
编辑:请注意,dbo.TableReturningFunc1和dbo.TableReturningFunc2是内联TVF语句,而不是多语句,或者它们是参数化视图语句。正在为p中的每一行重新计算您的过程 使用临时表实际上是缓存存储过程生成的结果集,因此无需重新计算 插入临时表很快,因为它不会生成重做/回滚 联接也很快,因为有一个稳定的resultset可以使用一个临时假脱机或一个工作表创建一个临时索引 您可以使用CTE在不使用临时表的情况下重用这些过程,但为了提高效率,SQL Server需要具体化CTE的结果 您可以尝试通过在子查询中使用ORDER来强制执行此操作:
WITH f1 AS
(
SELECT TOP 1000000000
A.ColumnX,
A.ColumnY
FROM dbo.TableReturningFunc1(@StaticParam1, @StaticParam2) AS A
ORDER BY
A.key
),
f2 AS
(
SELECT TOP 1000000000
B.ColumnX,
B.ColumnY,
FROM dbo.TableReturningFunc2(@StaticParam1, @StaticParam2) AS B
ORDER BY
B.Key
)
SELECT …
,这可能会导致优化器生成紧急假脱机
然而,这远远不能得到保证
保证的方法是向查询中添加一个选项使用计划,并将相应的ind CTE包装到Spool子句中
请参阅我的博客中关于如何做到这一点的文章:
这很难维护,因为每次重写查询时都需要重写您的计划,但这很有效
不过,使用临时表会容易得多。子查询引用外部查询是一个问题,这意味着必须为外部查询中的每一行编译和执行子查询。 您可以使用派生表,而不是使用显式临时表。 要简化您的示例,请执行以下操作:
SELECT P.Column1,
(SELECT [your XML transformation etc] FROM A where A.ID = P.ID) AS A
如果p包含10000条记录,则从A中选择A.ColumnX,其中A.ID=p.ID将执行10000次。
您可以改为使用派生表,如下所示:
SELECT P.Column1, A2.Column FROM
P LEFT JOIN
(SELECT A.ID, [your XML transformation etc] FROM A) AS A2
ON P.ID = A2.ID
好的,这不是说明性的伪代码,但基本思想与临时表相同,只是SQL Server在内存中完成了全部工作:它首先选择A2中的所有数据,并在内存中构造临时表,然后对其进行联接。这就省去了你自己选择它来临时工作的麻烦
只是给你一个例子,在另一种情况下,它可能更直接的意义的原则。考虑员工和缺勤信息,以显示每个员工的缺勤天数。
坏:运行的queryes数量与数据库中的员工数量相同
SELECT EmpName,
(SELECT SUM(absdays) FROM Absence where Absence.PerID = Employee.PerID) AS Abstotal
FROM Employee
好:只运行两个查询
SELECT EmpName, AbsSummary.Abstotal
FROM Employee LEFT JOIN
(SELECT PerID, SUM(absdays) As Abstotal
FROM Absence GROUP BY PerID) AS AbsSummary
ON AbsSummary.PerID = Employee.PerID
考虑将WITH common_table_表达式构造用于现在作为子选择或临时表的内容,请参阅
这没有多大意义,因为
似乎是插入一个
临时表,然后进行连接
de>越高,这没有多大意义,因为
似乎是插入一个
临时表,然后进行连接
默认为更高
对于临时表,您可以明确指示Sql Server使用哪个中间存储。但如果您将所有内容都隐藏在一个大查询中,Sql Server将自行决定。差别其实并没有那么大;在一天结束时,使用临时存储,无论您是否将其指定为临时表
在您的情况下,临时表工作得更快,为什么不坚持使用它们呢?使用中间临时表可能会加快查询速度,但在您的情况下,最可能的原因是正在调用但未列出的函数可能是多语句TVF,而不是内嵌TVF。多语句TVF对其调用查询的优化是不透明的,因此优化器无法判断是否存在重复使用数据或其他逻辑/物理运算符重新排序优化的机会。因此,它所能做的就是在每次包含查询产生另一个包含XML列的行时重新执行TVFs 简言之,多语句TVF阻碍了优化器 按照典型偏好的顺序,通常的解决方案是: 将有问题的多语句TVF重新编写为串联TVF 将函数代码插入调用查询,或 将有问题的TVF数据转储到临时表中。这就是你所做的。。。
如果temp表在您的特定实例中速度更快,则应该改用表变量 这里有一篇关于差异和性能影响的好文章:
这个答案需要与卡西诺的文章一起阅读 明智的ap 应用交叉应用,您可以强制缓存或内联TVF的快捷方式计算。此查询立即返回
SELECT *
FROM (
SELECT (
SELECT f.num
FOR XML PATH('fo'), ELEMENTS ABSENT
) AS x
FROM [20090528_tvf].t_integer i
cross apply (
select num
from [20090528_tvf].fn_num(9990) f
where f.num = i.num
) f
) q
--WHERE x IS NOT NULL -- covered by using CROSS apply
FOR XML AUTO
您还没有提供真正的结构,因此很难构建有意义的内容,但该技术也应该适用
如果您将Quassnoi文章中的多语句TVF更改为内联TVF,那么该计划将变得更快,至少一个数量级,并且该计划神奇地简化为我无法理解的内容—太简单了
CREATE FUNCTION [20090528_tvf].fn_num(@maxval INT)
RETURNS TABLE
AS RETURN
SELECT num + @maxval num
FROM t_integer
统计数字
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
(10 row(s) affected)
Table 't_integer'. Scan count 2, logical reads 22, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 2 ms.
我同意,临时桌是个好概念。当一个表中的行数增加(例如4000万行)时,我希望通过应用与其他表的联接来更新表中的多个列。在这种情况下,我始终希望使用公共表表达式使用case语句更新select语句中的列,现在,我的select语句结果集包含更新的行。使用case语句使用select语句将4000万条记录插入临时表需要21分钟,然后创建索引需要10分钟,因此我的insert和索引创建时间需要30分钟。然后,我将通过将临时表更新结果集与主表连接来应用更新。更新4000万条记录中的1000万条记录需要5分钟,因此我对1000万条记录的总体更新时间几乎需要35分钟,而从通用表表达式开始则需要5分钟。在这种情况下,我的选择是公共表表达式。如果可能,我如何在不显式执行临时表工作的情况下实现这一点?CTE的1:28临时表方法0:04的性能。比10+分钟好多了。。但仍然有数量级的差异。请发布您的存储过程,以便我检查性能好吗?您的文章中的注释已锁定,但我已添加了此问题的答案,以显示计划指南的替代方案,对于您在文章中演示的问题域,这并不能完全解决我遇到的问题。使用左外部联接并转换为使用FOR XML PATH以获得正确的嵌套,我得到了1:26的运行时。与temp table方法的0:04相比,这里是我现在要做的;将查询分解为各个组件,并在查询分析器中加载每个部分,然后选择ShowEstimated execution plan。然后对整个查询执行相同的操作。它应该能让你很好地了解哪一步慢,以及乐观者是如何解释你的请求的。查看它是否是子组件之一,或者仅当它全部放在一起时。寻找循环和扫描。最重要的是,您可能会发现一个步骤负责几乎所有的执行时间-看看您是否可以优化它。在SQL 2005及以上版本中,临时表的速度与表变量在绝大多数时间的速度一样快或更快。这是因为表变量不能有统计信息,所以对于查询优化器来说,它们在查询计划中看起来只有一行。因此,它们被错误地优化了,并且当它们的行数明显超过10行时,它们的性能往往会很差。本文使用了与您参考的文章类似的测试,但是puts显示了2005年当您在中放入更多行时会发生什么。噢,这篇文章是:嘿。实际上,它们都是同一篇文章!我也发现了这个。与临时表相比,使用表变量的性能不如临时表。我同意,除了TVF是内联的,基本上是参数化的视图之外。我将在问题中补充这一澄清。总的来说,这是个好建议。
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
(10 row(s) affected)
Table 't_integer'. Scan count 2, logical reads 22, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 2 ms.