SQL Server 2005:过程比函数慢得多

时间:2014-01-31 17:12:35

标签: sql-server tsql sql-server-2005 stored-procedures inline

编辑:
1.所有字符串文字现在都有N个,例如N''N'%'。这对时间没有影响 2.我提供了执行计划的链接。

功能执行计划:https://drive.google.com/file/d/0BxuNJKb4d8Y6elV1OHVUaWVRUU0/edit?usp=sharing

程序的执行计划:https://drive.google.com/file/d/0BxuNJKb4d8Y6cm9SSzFzNWdoVkE/edit?usp=sharing

我正在尝试重新实现内联表值函数作为存储过程,以便我可以动态控制我在搜索时比较哪些列(您可以在下面看到详细信息)。不幸的是,程序比函数慢很多(4-5)个数量级,这使得它无法使用,所以我来这里寻求指导。正如你从下面的程序中看到的那样,我已经考虑了参数嗅探(通过WITH RECOMPILE),我很确定我已经正确地考虑了ANSI_NULLS

我的问题是,“为什么这个存储过程比这个函数慢得多,如何加快速度至少同样快?”

在它出现之前,双通配符搜索非常慢的原因是底层视图由于多种原因而无法编入索引,例如使用LEFT OUTER JOIN,所以我不能采用更聪明的方法依赖索引。

以下块大约需要12秒。

EXEC [db_name].[schema_name].[procedure_name] 
@foo='richard',
@bar='j',
@spam='smith',
@eggs='jr';

以下块大约需要0秒。

SELECT * FROM [db_name].[schema_name].[function_name]
('richard','j','smith','jr')

Guts如下。两者的执行计划相当广泛,尽管我在SQL上没有足够的能力来理解他们所说的内容或他们的许多差异的影响。

我很高兴也能从视图中分享信息,如果相关的话;例如,我可以告诉你它没有编入索引,并且它包含使用stuff计算的列(因为我无法访问正确的连接聚合函数)和isnull,它使用{ {1}}。

老实说,我不知道可能有什么相关性,因为存储过程和函数都使用非常相似的代码在同一个视图上工作。

程序的胆量:

SELECT DISTINCT

功能之胆:

USE [db_name]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [schema_name].[procedure_name]
    @foo NVARCHAR(max) = null
    ,@bar NVARCHAR(max) = null
    ,@spam NVARCHAR(max) = null
    ,@eggs NVARCHAR(max) = null
WITH RECOMPILE
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @sqlcmd NVARCHAR(max);
    SET @sqlcmd = N'
        SELECT DISTINCT
        l.this,
        l.that,
        l.other
        FROM [db_name].[schema_name].[view_name] l
        WHERE 1=1';
    DECLARE @params NVARCHAR(max);
    SET @params = N'    
    @foo NVARCHAR(max) = null
    ,@bar NVARCHAR(max) = null
    ,@spam NVARCHAR(max) = null
    ,@eggs NVARCHAR(max) = null
    ';
    SET @foo = '%' + @foo + '%';
    SET @sqlcmd = @sqlcmd + CASE WHEN 
        @foo 
        IS NOT NULL THEN
        N' AND l.foo LIKE @foo'
        ELSE '' END;
    SET @bar = '%' + @bar + '%';
    SET @sqlcmd = @sqlcmd + CASE WHEN 
        @bar 
        IS NOT NULL THEN
        N' AND l.bar LIKE @bar'
        ELSE '' END;
    SET @spam = '%' + @spam + '%';
    SET @sqlcmd = @sqlcmd + CASE WHEN 
        @spam
        IS NOT NULL THEN
        N' AND l.spam LIKE @spam'
        ELSE '' END;
    SET @eggs = '%' + @eggs + '%';
    SET @sqlcmd = @sqlcmd + CASE WHEN 
        @eggs
        IS NOT NULL THEN
        N' AND l.eggs LIKE @eggs'
        ELSE '' END;
    EXECUTE sp_executesql @sqlcmd, @params, @foo, @bar, @spam, @eggs;
END

1 个答案:

答案 0 :(得分:0)

在你的函数中试试这个:

WHERE
  1 = CASE
    WHEN @foo IS NULL THEN 1
    WHEN l.foo LIKE ISNULL('%'+@foo+'%') THEN 1
  END
  AND 1 = CASE
    WHEN @bar IS NULL THEN 1
    WHEN l.bar LIKE ISNULL('%'+@bar+'%') THEN 1
  END
  AND 1 = CASE
    WHEN @spam IS NULL THEN 1
    WHEN l.foo LIKE ISNULL('%'+@spam+'%') THEN 1
  END
  AND 1 = CASE
    WHEN @eggs IS NULL THEN 1
    WHEN l.eggs LIKE ISNULL('%'+eggs+'%') THEN 1
  END