2017-07-07 62 views
1

我有它运行在一个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说:

的应用运算符的工作我n个以下方式产生用于FROM子句表源:针对left_table_source以产生行集的每一行

  1. 评估板right_table_source。

    right_table_source中的值取决于left_table_source。 right_table_source可以用这种方式表示:TVF(left_table_source.row),其中TVF是一个表值函数。

  2. 通过执行UNION ALL操作,将right_table_source评估中每行生成的结果集与left_table_source组合在一起。

    APPLY运算符的结果生成的列列表是来自left_table_source的列的集合,与来自right_table_source的列列表组合在一起。

这正是我想要的行为。但这不是SQL Server实际做的。

实际上会发生什么情况是,SQL Server查询优化器试图通过在WHERE子句中运行的远程服务器上一个SELECT FROM PI.piarchive..picount查询,没有tag = @elapsedTimeTagfilterexpr = <stuff>表达式来优化TVF子查询,然后做一个在SQL Server中加入以仅选择指定标签的数据。

不幸的是,这是不正确的优化。 picount表是包含数千个标签的底层数据库视图。如果tag = @elapsedTimeTag筛选条件未提供给远程服务器,则查询超时。并且filterexpr=<stuff>条件需要与标记条件匹配相同的标记名,以获得正确的答案。所以我真的需要为左边表中的每一行运行一个TVF远程子查询。

如何提示/强制SQL Server到实际上对CROSS APPLY的左侧表格中的每一行运行表值函数的查询一次,而不是当前正在执行的操作?

我试图重构外部查询首先明确选择标签列表,但它似乎并没有帮助:

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 
+0

你能发布执行计划吗?您链接的文档可能不适用于ITVF,因为优化器可以“内联”该函数 - 它可以内联函数的主体并运行您的查询,就像它是一个大选。这可能就是你所看到的这是因为你的服务器上有一个查询链接服务器数据的ITVF会更加复杂。当您直接在外部服务器实例上运行时,此查询的行为如何? –

+0

你希望看到执行计划的格式是什么? – Hydrargyrum

+0

如果我直接在外部服务器上运行TVF查询,提供一个标签名称作为参数,它就像我期望的那样工作。我认为外部服务器没有实现CROSS APPLY,所以外部查询不能直接运行。这部分必须在SQL Server中。 – Hydrargyrum

回答

1

重写dbo.fnGetResultsForTag为多语句表值函数(MTVF),而不是内联表值函数(ITVF)似乎具有禁止此“优化”的预期效果。

但是,它依赖于实现细节感觉很笨拙,所以我仍然对替代解决方案感兴趣,这些解决方案明确指示查询优化器不要“扁平化”ITVF子查询。

+0

这就是我最终做的。 – peterdn