使用表函数优化多个连接
Posted
技术标签:
【中文标题】使用表函数优化多个连接【英文标题】:Optimize multiple joins with table functions 【发布时间】:2009-09-29 13:30:18 【问题描述】:我想在同一个查询中为不同的输入变量多次加入同一个表函数。但在我的情况下,这比使用表变量和分别从表函数中选择要慢得多。
我怎样才能避免表变量并且仍然有一个快速的查询?
例如,我们有一个类似的 SQL 查询
选择 P.ProjectName、A.Number、B.Number 来自项目 AS P 左加入 dbo.fn_ProjectNumber(@dateA) 作为 A ON P.ProjectID = A.ProjectID 左连接 dbo.fn_ProjectNumber(@dateB) AS B ON P.ProjectID = B.ProjectID但它比从函数中单独选择到变量中然后再加入要慢得多,例如:
插入@tempA 选择 P.ProjectID、A.Number 来自项目 AS P 左加入 dbo.fn_ProjectNumber(@dateA) 作为 A ON P.ProjectID = A.ProjectID 插入@tempB 选择 P.ProjectID、B.Number 来自项目 AS P 左连接 dbo.fn_ProjectNumber(@dateB) AS B ON P.ProjectID = B.ProjectID 选择 P.ProjectName、A.Number、B.Number 来自项目 AS P 左加入 @tempA 作为 A ON P.ProjectID = A.ProjectID 左加入 @tempA 作为 B ON P.ProjectID = B.ProjectID这可能是什么原因?有没有办法可以快速查询并避免表变量?
更多细节:
这只是一个类似于我正在做的例子,但函数fn_ProjectNumber(@date datetime)
将包含四个表之间的连接......
【问题讨论】:
您没有提供太多信息。如何显示函数中的代码、执行计划(SET SHOWPLAN_ALL ON)等... 【参考方案1】:尝试修复连接,您在第二个 LEFT JOIN 中引用了错误的别名:
原文:
SELECT P.ProjectName, A.Number, B.Number
FROM Project AS P
LEFT JOIN dbo.fn_ProjectNumber(@dateA) AS A
ON P.ProjectID = A.ProjectID
LEFT JOIN dbo.fn_ProjectNumber(@dateB) AS B
ON P.ProjectID = A.ProjectID
已修复:
SELECT P.ProjectName, A.Number, B.Number
FROM Project AS P
LEFT JOIN dbo.fn_ProjectNumber(@dateA) AS A
ON P.ProjectID = A.ProjectID
LEFT JOIN dbo.fn_ProjectNumber(@dateB) AS B
ON P.ProjectID = B.ProjectID --<<<<<
【讨论】:
啊,在我的示例中这是一个错误。在实际情况下,别名是正确的......【参考方案2】:您有什么特别的原因要避免使用表变量吗?它们可以是一种很好的优化技术,并且不会留下任何临时对象需要清理。
无论如何,如果你不想那样做,你可以随时尝试
SELECT
P.ProjectID, A.Number, B.Number
FROM
Project AS P
LEFT JOIN
(SELECT P.ProjectID, A.Number
FROM Project AS P
LEFT JOIN dbo.fn_ProjectNumber(@dateA) AS A
ON P.ProjectID = A.ProjectID
) AS A
ON P.ProjectID = A.ProjectID
LEFT JOIN
(SELECT P.ProjectID, B.Number
FROM Project AS P
LEFT JOIN dbo.fn_ProjectNumber(@dateB) AS B
ON P.ProjectID = B.ProjectID
) AS B
ON P.ProjectID = B.ProjectID
【讨论】:
【参考方案3】:连接速度很慢,因为在您的示例查询中,您为表 Project 中的每一行调用每个函数一次。使用临时表会更快,因为您只调用一次函数。
避免使用临时表的一种方法是使用 CTE(常见的表表达式——它们不仅用于递归——在 SQL 2005 及更高版本中可用。)。一般语法类似于:
WITH cteTempName (<list of columns>)
as (<your table function call>)
SELECT <your query here, with "cteTempName" appearing as just another table to select from>
【讨论】:
是什么让您认为该函数是为每一行调用的?代码:LEFT JOIN dbo.fn_ProjectNumber(@dateA) AS A ON P.ProjectID = A.ProjectID 对我来说似乎只被调用一次。 嗯,没错。我基于这样的语句,即查询运行速度比填充临时表时慢得多,因为这通常是正在发生的事情——但你是对的,它们不应该经常被调用。慢多少?查看查询计划会很有用。【参考方案4】:也许连接速度较慢,因为您没有定义连接在一起的表之间的关系? 我对 SQL Server 中查询的性能了解不多,但是定义关系会提高连接的性能。
【讨论】:
以上是关于使用表函数优化多个连接的主要内容,如果未能解决你的问题,请参考以下文章