有没有更快的方法来 运行 一个 SQL Where Case

Is there a faster way to run an SQL Where Case

我有以下存储过程(在 MS SQL 中):

ALTER PROCEDURE [dbo].[proc_GetWorksWithEngineerVisits3]
    @sTextSearch nvarchar(255) = NULL,
    @bCompleteFlag bit = NULL,
    @dExpectedStartDateTime datetime = NULL,
    @dExpectedEndDateTime datetime = NULL,
    @sResponsible_UserIDs nvarchar(255) = NULL,
    @bEnableTextSearchFilter bit = false,
    @bEnableCompleteFlagFilter bit = false,
    @bEnableExpectedDateTimeRangeFilter bit = false,
    @bEnableResponsible_UserIDFilter bit = false
AS
    SELECT *
    FROM dbo.vwWorksWithEngineerVisits
    WHERE
        --TextSearch Filter Start
        (sCustomer LIKE CASE
                           WHEN @bEnableTextSearchFilter = 1
                              THEN '%' + @sTextSearch + '%'
                              ELSE sCustomer
                        END
         OR
         sSite LIKE CASE
                       WHEN @bEnableTextSearchFilter = 1
                          THEN '%' + @sTextSearch + '%'
                          ELSE sSite
                    END
         OR
         sCallID LIKE CASE
                         WHEN @bEnableTextSearchFilter = 1
                            THEN '%' + @sTextSearch + '%'
                            ELSE sCallID
                      END)
    --TextSearch Filter End
    AND

    --Complete Filter Start
    bIsComplete = CASE 
                     WHEN @bEnableCompleteFlagFilter = 1
                        THEN @bCompleteFlag
                        ELSE bIsComplete
                  END
    --Complete Filter End

    AND

    --Expected DateTime Range Filter Start
    dExpectedStartDateTime >= CASE 
                                 WHEN @bEnableExpectedDateTimeRangeFilter = 1
                                    THEN @dExpectedStartDateTime
                                    ELSE dExpectedStartDateTime
                              END

AND

dExpectedEndDateTime <= 
CASE 
    WHEN @bEnableExpectedDateTimeRangeFilter = 1
    THEN @dExpectedEndDateTime
    ELSE dExpectedEndDateTime
END
----Expected DateTime Range Filter End

AND

--Responsible_UserID Filter Start

lResponsible_UserID in (
CASE 
    WHEN @bEnableResponsible_UserIDFilter = 0
    THEN lResponsible_UserID
    ELSE (SELECT Value FROM dbo.CSVToList(@sResponsible_UserIDs) AS CSVToList_1) 
END
)
--Responsible_UserID Filter End

ORDER BY dExpectedEndDateTime

输出很好,但是很慢(只有 5000 行需要 15 秒)同样的数字直接执行 dbo.vwWorksWithEngineerVisits 需要 1 秒。执行 SP 时,我将所有启用标志设置为 0。

DECLARE @return_value int

EXEC    @return_value = [dbo].[proc_GetWorksWithEngineerVisits3]
        @sTextSearch = NULL,
        @bCompleteFlag = False,
        @dExpectedStartDateTime = N'01/01/1969',
        @dExpectedEndDateTime = N'01/01/2021',
        @sResponsible_UserIDs = NULL,
        @bEnableTextSearchFilter = 0,
        @bEnableCompleteFlagFilter = 0,
        @bEnableExpectedDateTimeRangeFilter = 0,
        @bEnableResponsible_UserIDFilter = 0

SELECT  'Return Value' = @return_value

如果设置了相应的标志,我希望能够只过滤一列。我可能只检查主要参数中的 NULL 并减少参数,但我认为这不会改变我遇到的问题。

前 4 个案例过滤器非常基本,当我评论剩下的最后 3 个时,performance/result 是即时的。一旦我将最后 3 个中的一个添加回混音,事情就会像上面那样变慢。它们的不同之处在于它们执行“>=”或“in”,而不仅仅是“=”或“like”。我注意到的另一件事是,当我更改以下内容时:

lResponsible_UserID in (
CASE 
    WHEN @bEnableResponsible_UserIDFilter = 0
    THEN lResponsible_UserID
    ELSE (SELECT Value FROM dbo.CSVToList(@sResponsible_UserIDs) AS CSVToList_1) 
END

lResponsible_UserID in (
CASE 
    WHEN @bEnableResponsible_UserIDFilter = 0
    THEN lResponsible_UserID
    ELSE lResponsible_UserID
END

这也加快了 1 秒的速度。当标志始终为 0 时,更改语句的 else 部分怎么会有所不同,所以永远不应该 运行?

我需要这些过滤器,而且我需要它们是动态的。有多种运算符类型(包括以函数为目标的 IN)。有没有一种方法可以重构这个存储过程以获得相同的结果(它确实有效),但以一种更加可选的方式?

如果我在 post 中遗漏了一些内容,我深表歉意,如果指出,我会进行编辑。

谢谢

这是一个很大的查询!

SQL 服务器在您定义它时针对您的 sp 中的查询运行编译器。然后它使用该编译过程,愉快地忽略可能来自您的特定参数值的任何优化。 This page explains:

When SQL Server executes procedures, any parameter values that are used by the procedure when it compiles are included as part of generating the query plan. If these values represent the typical ones with which the procedure is subsequently called, then the procedure benefits from the query plan every time that it compiles and executes. If parameter values on the procedure are frequently atypical, forcing a recompile of the procedure and a new plan based on different parameter values can improve performance.

在你的情况下,你的参数设置极大地简化了你想要的搜索。但是编译后的sp并不知道这一点,所以它使用了一个过度泛化的搜索计划。

尝试将 this 附加到您的 SP 中的查询(在您的 ORDER BY 子句之后)以强制生成一个新的、希望更具体的执行计划。

OPTION (RECOMPILE)

此外,您可以整理您的过滤器子句,使它们不那么粗糙。

为您的 text-search 个案例试试这个:更改

    sCustomer LIKE CASE
                   WHEN @bEnableTextSearchFilter = 1
                          THEN '%' + @sTextSearch + '%'
                          ELSE sCustomer
                    END

  (@bEnableTextSearchFilter <> 1 OR sCustomer LIKE '%' + @sTextSearch + '%')

当您的过滤器被禁用时,这将避免说 column LIKE column,并且可能会节省一些时间。

您也可以将相同的原则应用于其余的 CASE 语句。

注意:过滤器模式 column LIKE '%value%' 本质上 慢;它不能在 column 上使用索引范围扫描,因为 text-matching 没有锚定在模式的开头。相反,它必须扫描所有值。