T-SQL在速度上调用标量函数

时间:2013-12-12 11:47:19

标签: sql sql-server tsql

我正在使用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,但我可以更改该功能。是否有任何方法可以修改该功能以加快速度?我很感激你的帮助。感谢

3 个答案:

答案 0 :(得分:1)

如果您仍想使用某个功能但没有它,则使用内联表值版本。它快了很多。查看一些专家的这些文章。

http://aboutsqlserver.com/2011/10/23/sunday-t-sql-tip-inline-vs-multi-statement-table-valued-functions/

http://dataeducation.com/scalar-functions-inlining-and-performance-an-entertaining-title-for-a-boring-post/

我有几个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提示。