Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/80.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 为什么插入和连接临时表更快?_Sql_Sql Server_Sql Server 2005_Tsql - Fatal编程技术网

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.