我正在使用SQL Server 2008,我有以下查询:
SELECT [Id] FROM [dbo].[Products] WHERE [dbo].GetNumOnOrder([Id]) = 0
使用以下“GetNumOnOrder”标量值函数:
CREATE FUNCTION [dbo].[GetNumOnOrder]
(
@ProductId INT
)
RETURNS INT
AS
BEGIN
DECLARE @NumOnOrder INT
SELECT @NumOnOrder = SUM([NumOrdered] - [NumReceived])
FROM [dbo].[PurchaseOrderDetails]
INNER JOIN [dbo].[PurchaseOrders]
ON [PurchaseOrderDetails].[PurchaseOrderId] = [PurchaseOrders].[Id]
WHERE [PurchaseOrders].[StatusId] <> 5
AND [PurchaseOrderDetails].[ProductId] = @ProductId
RETURN CASE WHEN @NumOnOrder IS NOT NULL THEN @NumOnOrder ELSE 0 END
END
然而,执行需要大约6秒钟。不幸的是我无法控制生成的初始SQL,但我可以更改该功能。是否有任何方法可以修改该功能以加快速度?我很感激你的帮助。感谢
答案 0 :(得分:1)
如果您仍想使用某个功能但没有它,则使用内联表值版本。它快了很多。查看一些专家的这些文章。
我有几个MVP朋友说这是他们写的唯一一个函数,因为标量函数被视为一堆存储过程调用。
使用内联表值函数重写。检查语法,因为我没有。使用Coalesce函数将NULL转换为Zero。
--
-- Table value function
--
CREATE FUNCTION [dbo].[GetNumOnOrder] ( @ProductId INT )
RETURNS TABLE
AS
RETURN
(
SELECT
COALESCE(SUM([NumOrdered] - [NumReceived]), 0) AS Num
FROM
[dbo].[PurchaseOrderDetails]
INNER JOIN [dbo].[PurchaseOrders]
ON [PurchaseOrderDetails].[PurchaseOrderId] = [PurchaseOrders].[Id]
WHERE [PurchaseOrders].[StatusId] <> 5
AND [PurchaseOrderDetails].[ProductId] = @ProductId
);
--
-- Sample call with cross apply
--
SELECT [Id]
FROM [dbo].[Products] P
CROSS APPLY [dbo].[GetNumOnOrder] (C.Id) AS CI
WHERE CI.Num = 0;
答案 1 :(得分:1)
如果您有权向表中添加索引(并且取决于您正在使用的SQL Server的版本),我将调查添加以下内容的性能增益: -
create index newindex1 on PurchaseOrders (id)
include (StatusId);
create index newindex2 on PurchaseOrderDetails (PurchaseOrderId)
include (ProductId,NumOrdered,NumReceived);
您可能已经在这些列上有索引 - 但上面的索引将以最有效的方式支持函数中的查询(将页面读取次数减少到最少)。如果此函数的性能足够重要,您还可以考虑在表中添加计算列 - 对于NumOrdered-NumReceived
(然后仅在上面的索引中包含结果列 - 以及您的查询)。您还可以考虑在索引视图而不是表中执行此操作 - 但绑定视图的架构可能会令人厌烦且不方便。显然,所讨论的表格越广泛 - 性能的提升就越大。
答案 2 :(得分:0)
如果表PurchaseOrderDetails
中的数据分布不均匀,则缓存的查询计划可能会影响您的查询性能。这是“Parameter Sniffing
”可能会创建错误查询计划的情况。实际上,SQL Server支持称为“参数嗅探”的优化,它将根据@ProductId
变量中的特定值选择不同的计划。
因此,为了提高查询性能,您可以将函数重写为:
CREATE FUNCTION [dbo].[GetNumOnOrder]
(
@ProductId INT
)
RETURNS INT
AS
BEGIN
DECLARE @NumOnOrder INT,@v_ProductId INT
SET @v_ProductId = @ProductId;
SELECT @NumOnOrder = SUM([NumOrdered] - [NumReceived])
FROM [dbo].[PurchaseOrderDetails]
INNER JOIN [dbo].[PurchaseOrders]
ON [PurchaseOrderDetails].[PurchaseOrderId] = [PurchaseOrders].[Id]
WHERE [PurchaseOrders].[StatusId] <> 5
AND [PurchaseOrderDetails].[ProductId] = @v_ProductId
RETURN CASE WHEN @NumOnOrder IS NOT NULL THEN @NumOnOrder ELSE 0 END
END
或者您可以包含Recomplie提示。