【问题标题】:How can I work around SQL Server - Inline Table Value Function execution plan variation based on parameters?如何解决 SQL Server - 基于参数的内联表值函数执行计划变化?
【发布时间】:2010-10-01 19:04:32
【问题描述】:

情况如下:
我有一个带有日期时间参数的表值函数,以免说 tdf(p_date) , 过滤大约 200 万行,选择列日期小于 p_date 的行,并在其他列上计算一些聚合值。
它工作得很好,但如果 p_date 是一个自定义标量值函数(在我的例子中返回一天结束),执行计划就会改变,查询从 1 秒到 1 分钟执行时间。

概念证明表 - 1K 产品,2M 行:

CREATE TABLE [dbo].[POC](
    [Date] [datetime] NOT NULL,
    [idProduct] [int] NOT NULL,
    [Quantity] [int] NOT NULL
) ON [PRIMARY]

内联表值函数:

CREATE FUNCTION tdf (@p_date datetime)
RETURNS TABLE 
AS
RETURN 
(
    SELECT idProduct, SUM(Quantity) AS TotalQuantity,
         max(Date) as LastDate
    FROM POC
    WHERE (Date < @p_date)
    GROUP BY idProduct
)

标量值函数:

CREATE FUNCTION [dbo].[EndOfDay] (@date datetime)
RETURNS datetime
AS
BEGIN
    DECLARE @res datetime
    SET @res=dateadd(second, -1,
         dateadd(day, 1, 
             dateadd(ms, -datepart(ms, @date),
                 dateadd(ss, -datepart(ss, @date),
                    dateadd(mi,- datepart(mi,@date),
                         dateadd(hh, -datepart(hh, @date), @date))))))
    RETURN @res
END

查询 1 - 效果很好

SELECT * FROM [dbo].[tdf] (getdate())

执行计划结束: 流聚合成本 13%

查询 2 - 不太好

SELECT * FROM [dbo].[tdf] (dbo.EndOfDay(getdate()))

执行计划结束: 流聚合成本 4%

【问题讨论】:

  • SQL 代码来准确说明您的意思会很有帮助。
  • 我发布了复制执行计划变体所需的代码

标签: sql-server tsql sql-execution-plan user-defined-functions


【解决方案1】:

开销是你的标量函数。

这里的 TVF 像内联宏一样展开,所以

SELECT * FROM [dbo].[tdf] (getdate())

变成

SELECT     idProduct, SUM(Quantity) AS TotalQuantity, max(Date) as LastDate
    FROM         POC
    WHERE     Date < getdate()
    GROUP BY idProduct

当您使用日终标量函数时,SQL 无法将 EOD(GETDATE()) 评估为常量。 抱歉,我无法快速找到关于 SQL 如何评估这些内容的文章。

我猜它是针对每一行进行评估的,而不是你想要的预先评估。

我会单独计算 EOD 声明:

DECLARE @eod datetime;
SET @eod = dbo.EndOfDay(getdate());
SELECT * FROM [dbo].[tdf] (@eod)

我也将它用于 EOD 功能:

DATEADD(second, -1, DATEADD(day, 1, (DATEDIFF(day, 0, @date))))

EDIT: Other question I answered

【讨论】:

  • 同意标量函数几乎总是逐行运行,因此应该避免。
【解决方案2】:

您也可以将 EndOfDay 重写为内联 UDF,并使用嵌套的内联 UDF。例子:

Many nested inline UDFs are very fast

Calculating third Wednesday of the month with inline UDFs

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多