情况如下:
我有一个带有datetime参数的表值函数,以至于说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%&lt; ---聚集索引扫描成本86%
查询2 - 不太好
SELECT * FROM [dbo].[tdf] (dbo.EndOfDay(getdate()))
执行计划的结束: 流聚合成本4%&lt; ---过滤成本12%&lt; ---集群索引扫描成本86%
答案 0 :(得分:6)
开销是你的标量函数。
这里的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))))
答案 1 :(得分:1)
您也可以将EndOfDay重写为内联UDF,并使用嵌套的内联UDF。例子: