有没有更快的方法来 运行 一个 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 没有锚定在模式的开头。相反,它必须扫描所有值。
我有以下存储过程(在 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 没有锚定在模式的开头。相反,它必须扫描所有值。