如何防止使用内联 Table 值函数对 CROSS APPLY 进行不良优化
How to prevent bad optimization of CROSS APPLY with Inline Table-Valued Function
我有一个内联 table 值函数,它 运行 是 OLE-DB 链接服务器上的查询,定义如下:
CREATE FUNCTION [dbo].[fnGetResultsForTag]
(
@elapsedTimeTag NVARCHAR(50)
)
RETURNS TABLE
AS
RETURN
(
SELECT tag, time, value
FROM PI.piarchive..picount
WHERE tag = @elapsedTimeTag
AND filterexpr = QUOTENAME(@elapsedTimeTag, '''') + ' > NEXTVAL(' + QUOTENAME(@elapsedTimeTag, '''') + ', ''*'')'
AND time BETWEEN (SELECT result FROM PI.pifunction..date WHERE arg1='t-20h') AND (SELECT result FROM PI.pifunction..date WHERE arg1='*')
AND timestep = (SELECT result FROM PI.pifunction..time WHERE arg1='12h')
AND calcbasis = 'EventWeighted'
)
GO
它是从这样的存储过程中调用的:
SELECT results.*
FROM PI.pipoint..pipoint2 points
CROSS APPLY dbo.fnGetResultsForTag(points.tag) AS results
WHERE points.tag LIKE @TagPattern
ORDER BY time ASC, tag ASC
MS documentation 表示:
The APPLY operator works in the following way to produce the table source for the FROM clause:
Evaluates right_table_source against each row of the left_table_source to produce rowsets.
The values in the right_table_source depend on left_table_source. right_table_source can be represented approximately this way: TVF(left_table_source.row), where TVF is a table-valued function.
Combines the result sets that are produced for each row in the evaluation of right_table_source with the left_table_source by performing a UNION ALL operation.
The list of columns produced by the result of the APPLY operator is the set of columns from the left_table_source that is combined with the list of columns from the right_table_source.
这正是我想要的行为。但这不是 SQL 服务器实际在做什么。
实际发生的是 SQL 服务器查询优化器试图通过 运行ning one SELECT FROM PI.piarchive..picount
查询来优化 TVF 子查询在远程服务器上,without WHERE 子句中的 tag = @elapsedTimeTag
和 filterexpr = <stuff>
表达式,然后在 SQL 服务器中加入到 select 仅指定标签的数据。
不幸的是,这是一个不正确的优化。 picount table 是包含数千个标签的基础数据库的视图。如果 tag = @elapsedTimeTag
过滤条件未提供给远程服务器,则查询超时。并且 filterexpr=<stuff>
条件需要匹配与标记条件相同的标记名,才能获得正确答案。所以我真的需要 运行 为左侧的每一行 table 一个 TVF 远程子查询 table。
我如何 hint/force SQL 服务器 实际上 运行 table 值函数的每行查询一次在 CROSS APPLY 的左侧 table,而不是它当前正在做什么?
我已经尝试重构外部查询以首先显式 select 标签列表,但它似乎没有帮助:
DECLARE @TagNames AS TABLE (tag NVARCHAR(50) NOT NULL)
INSERT INTO @TagNames
SELECT tag FROM PI.pipoint..pipoint2 WHERE tag LIKE @TagPattern
SELECT results.*
FROM @TagNames t
CROSS APPLY dbo.fnGetResultsForTag(t.tag) AS results
ORDER BY time ASC, tag ASC
将 dbo.fnGetResultsForTag 重写为多语句 Table 值函数 (MTVF),而不是内联 Table 值函数 (ITVF),似乎具有禁止此 "optimization".
但是依赖实现细节感觉很尴尬,所以我仍然对明确指示查询优化器不要 "flatten" ITVF 子查询的替代解决方案感兴趣。
我有一个内联 table 值函数,它 运行 是 OLE-DB 链接服务器上的查询,定义如下:
CREATE FUNCTION [dbo].[fnGetResultsForTag]
(
@elapsedTimeTag NVARCHAR(50)
)
RETURNS TABLE
AS
RETURN
(
SELECT tag, time, value
FROM PI.piarchive..picount
WHERE tag = @elapsedTimeTag
AND filterexpr = QUOTENAME(@elapsedTimeTag, '''') + ' > NEXTVAL(' + QUOTENAME(@elapsedTimeTag, '''') + ', ''*'')'
AND time BETWEEN (SELECT result FROM PI.pifunction..date WHERE arg1='t-20h') AND (SELECT result FROM PI.pifunction..date WHERE arg1='*')
AND timestep = (SELECT result FROM PI.pifunction..time WHERE arg1='12h')
AND calcbasis = 'EventWeighted'
)
GO
它是从这样的存储过程中调用的:
SELECT results.*
FROM PI.pipoint..pipoint2 points
CROSS APPLY dbo.fnGetResultsForTag(points.tag) AS results
WHERE points.tag LIKE @TagPattern
ORDER BY time ASC, tag ASC
MS documentation 表示:
The APPLY operator works in the following way to produce the table source for the FROM clause:
Evaluates right_table_source against each row of the left_table_source to produce rowsets.
The values in the right_table_source depend on left_table_source. right_table_source can be represented approximately this way: TVF(left_table_source.row), where TVF is a table-valued function.
Combines the result sets that are produced for each row in the evaluation of right_table_source with the left_table_source by performing a UNION ALL operation.
The list of columns produced by the result of the APPLY operator is the set of columns from the left_table_source that is combined with the list of columns from the right_table_source.
这正是我想要的行为。但这不是 SQL 服务器实际在做什么。
实际发生的是 SQL 服务器查询优化器试图通过 运行ning one SELECT FROM PI.piarchive..picount
查询来优化 TVF 子查询在远程服务器上,without WHERE 子句中的 tag = @elapsedTimeTag
和 filterexpr = <stuff>
表达式,然后在 SQL 服务器中加入到 select 仅指定标签的数据。
不幸的是,这是一个不正确的优化。 picount table 是包含数千个标签的基础数据库的视图。如果 tag = @elapsedTimeTag
过滤条件未提供给远程服务器,则查询超时。并且 filterexpr=<stuff>
条件需要匹配与标记条件相同的标记名,才能获得正确答案。所以我真的需要 运行 为左侧的每一行 table 一个 TVF 远程子查询 table。
我如何 hint/force SQL 服务器 实际上 运行 table 值函数的每行查询一次在 CROSS APPLY 的左侧 table,而不是它当前正在做什么?
我已经尝试重构外部查询以首先显式 select 标签列表,但它似乎没有帮助:
DECLARE @TagNames AS TABLE (tag NVARCHAR(50) NOT NULL)
INSERT INTO @TagNames
SELECT tag FROM PI.pipoint..pipoint2 WHERE tag LIKE @TagPattern
SELECT results.*
FROM @TagNames t
CROSS APPLY dbo.fnGetResultsForTag(t.tag) AS results
ORDER BY time ASC, tag ASC
将 dbo.fnGetResultsForTag 重写为多语句 Table 值函数 (MTVF),而不是内联 Table 值函数 (ITVF),似乎具有禁止此 "optimization".
但是依赖实现细节感觉很尴尬,所以我仍然对明确指示查询优化器不要 "flatten" ITVF 子查询的替代解决方案感兴趣。