【问题标题】:Why Query performance improve by calling UDF using subquery?为什么通过使用子查询调用 UDF 来提高查询性能?
【发布时间】:2018-11-17 20:47:13
【问题描述】:

我最近意识到,通过在子查询中调用 UDF 比直接调用它们执行得更好,为什么会发生这种情况?

举个例子:

CREATE FUNCTION [CurrentYearStart]()
RETURNS DATETIME
AS
BEGIN
    DECLARE @Date DATETIME;
    SELECT top 1 @Date = StartPeriod
    FROM SystemPeriods WITH (NOLOCK);
    RETURN @Date;
END

ProviderServiceAreas 有 412585 行。

以下查询在大约 50 秒后返回

SELECT   PayGroup, SystemType, MAX(EffDt) AS MaxEffDt
FROM     CAT.ProviderServiceAreas
WHERE    EffDt > CurrentYearStart() 
GROUP BY Paygroup, SystemType 

执行计划:https://www.brentozar.com/pastetheplan/?id=BJCom7vgm

以下查询在大约 1s 内返回

SELECT   PayGroup, SystemType, MAX(EffDt) AS MaxEffDt
FROM     CAT.ProviderServiceAreas
WHERE    EffDt > (SELECT CurrentYearStart()) 
GROUP BY Paygroup, SystemType 

执行计划:https://www.brentozar.com/pastetheplan/?id=HkolVQDx7

【问题讨论】:

  • 两者的执行计划是什么?
  • I noticed that by calling the function in the subquery they only execute the function once => 缓存 :) 无论如何你的函数非常危险(没有 ORDER BY + NOLOCK 的 TOP 1)
  • 无论如何我都会摆脱这个功能。众所周知,标量函数的性能很差。将其转换为内联表值函数或仅执行子查询。
  • 它并不是真正的缓存。这意味着该函数被多次执行并重放缓存的结果。我会想象更多不同的树形,它会评估一次函数,然后将结果用作相关参数。

标签: sql sql-server performance tsql user-defined-functions


【解决方案1】:

我会去:

SELECT PayGroup, SystemType, MAX(EffDt) AS MaxEffDt
FROM ServiceAreas CROSS JOIN
     (SELECT StartPeriod() as sp) sp
WHERE EffDt > sp.sp
GROUP BY Paygroup, SystemType ;

显然,性能差异是函数被调用的次数。如果您将函数调用放在FROM 子句中,您将拥有更多控制权。

【讨论】:

  • 绝对不是,我测试了,我得到了一个与最坏情况非常相似的执行计划,大约在 50 秒的时间..
  • @IsraelGarcia 。 . .这真的很奇怪。函数本身会不会出现性能问题,有时需要很长时间,有时不需要?
  • Gordon 函数是对包含 1 条记录的表的非常简单的调用,问题是因为我放置子查询时的执行计划与直接调用函数完全不同。检查我在问题文本中添加的执行计划。
猜你喜欢
  • 1970-01-01
  • 2017-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
  • 2012-10-03
相关资源
最近更新 更多