我的查询类似于
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')
P有~5000行 每个A和B~4000行
此查询的运行时性能约为10 +分钟。
然而将其更改为:
SELECT
P.Column1,
P.Column2,
P.Column3,
...
INTO #P
SELECT
A.ColumnX,
A.ColumnY,
...
INTO #A
FROM
dbo.TableReturningFunc1(@StaticParam1, @StaticParam2) AS A
SELECT
B.ColumnX,
B.ColumnY,
...
INTO #B
FROM
dbo.TableReturningFunc2(@StaticParam1, @StaticParam2) AS B
SELECT
P.Column1,
P.Column2,
P.Column3,
...
(
SELECT
A.ColumnX,
A.ColumnY,
...
FROM
#A AS A
WHERE
A.Key = P.Key
FOR XML AUTO, TYPE
),
(
SELECT
B.ColumnX,
B.ColumnY,
...
FROM
#B AS B
WHERE
B.Key = P.Key
FOR XML AUTO, TYPE
)
FROM #P AS P
FOR XML AUTO,ROOT('ROOT')
性能约为4秒。
这没有多大意义,因为看起来插入临时表然后默认情况下连接的成本应该更高。我倾向于SQL正在使用子查询执行错误的“连接”类型,但也许我错过了它,没有办法指定连接类型用于相关子查询。
有没有办法在没有通过索引和/或提示使用#temp tables / @ table变量的情况下实现这一目标?
编辑:请注意,dbo.TableReturningFunc1和dbo.TableReturningFunc2是内联TVF,而不是多语句,或者它们是“参数化”视图语句。
答案 0 :(得分:15)
正在为P
中的每一行重新评估您的程序。
对临时表执行的操作实际上是缓存存储过程生成的结果集,因此无需重新评估。
快速插入临时表是因为它不会生成redo
/ rollback
。
联接也很快,因为拥有稳定的结果集可以创建一个Eager Spool
或Worktable
的临时索引
您可以使用CTE
重复使用没有临时表的过程,但为了提高效率,SQL Server
需要实现CTE
的结果。
你可以尝试强制它在子查询中使用ORDER BY
来执行此操作:
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 …
,这可能会导致优化程序生成Eager Spool
。
然而,这远非得到保证。
保证的方法是在查询中添加OPTION (USE PLAN)
并将对应的CTE
包装到Spool
子句中。
请参阅我的博客中有关如何执行此操作的条目:
这很难维护,因为每次重写查询时都需要重写计划,但这样做效果很好而且非常有效。
但是,使用临时表会更容易。
答案 1 :(得分:4)
这个答案需要与Quassnoi的文章一起阅读 http://explainextended.com/2009/05/28/generating-xml-in-subqueries/
通过明智地应用CROSS APPLY,您可以强制内联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,计划变得更快(至少一个数量级),计划神奇地减少到我无法理解的东西(这太简单了!)。 / p>
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.
答案 2 :(得分:2)
子查询引用外部查询时出现问题,这意味着必须为外部查询中的每一行编译和执行子查询。 您可以使用派生表,而不是使用显式临时表。 为简化您的示例:
SELECT P.Column1,
(SELECT [your XML transformation etc] FROM A where A.ID = P.ID) AS A
如果P包含10,000条记录,则SELECT A.ColumnX FROM A,其中A.ID = P.ID将执行10,000次。
您可以改为使用派生表:
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”中的所有数据并构造临时表在内存中,然后加入它。这样您就不必自己选择TEMP。
只是为了给你一个在另一个背景下的原则的例子,它可能会更直接的意义。考虑员工和缺勤信息,您希望显示每位员工记录的缺勤天数。
错误:(运行与数据库中的员工一样多的查询)
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
答案 3 :(得分:1)
使用中间临时表可能会加速查询有几个可能的原因,但在您的情况下,最有可能的是被调用(但未列出)的函数可能是多语句TVF,而不是-line TVF。多语句TVF对其调用查询的优化是不透明的,因此优化器无法判断是否存在重用数据或其他逻辑/物理运算符重新排序优化的机会。因此,每次包含的查询应该生成带有XML列的另一行时,它所能做的就是重新执行TVF。
简而言之,多语句TVF挫败了优化者。
按(典型)偏好顺序的常用解决方案是:
答案 4 :(得分:0)
考虑将WITH common_table_expression
构造用作现在的子选择或临时表,请参阅http://msdn.microsoft.com/en-us/library/ms175972(SQL.90).aspx。
答案 5 :(得分:0)
这并不是很有意义 似乎是插入一个的成本 临时表,然后做连接应该 de>更高这没有多大意义 似乎是插入一个的成本 临时表,然后做连接应该 默认情况下会更高。
使用临时表,您可以指示Sql Server使用哪个中间存储。但是如果你把所有东西存放在一个大查询中,Sql Server将自行决定。差别并不是那么大;在一天结束时,使用临时存储,无论您是否将其指定为临时表。
在您的情况下,临时表的工作速度更快,为什么不坚持使用呢?
答案 6 :(得分:0)
答案 7 :(得分:-2)
如果临时表在特定实例中变得更快,则应改为使用表变量。
这里有一篇关于差异和绩效影响的好文章: