优化存储过程查询

问题描述 投票:0回答:1

请参阅下面的存储过程。

它由一个应用程序使用,该应用程序进行搜索,获取用户输入的订单并显示它们。 大多数时候,它会在几秒钟内返回预期结果。 然而,当搜索特定供应商时,它会在 5-10 秒内返回答案。 如果它包含@vendorsOnly = true,那么速度非常快。 如果@vendorsOnly = 0,那么速度很慢。

我尝试在 VendorId 上放置索引,这似乎有一段时间有帮助,但它又运行缓慢了。

我认为这里存在优化问题,但我不确定应该做什么。

ALTER PROCEDURE [dbo].[SearchOrderInfos_v21]
(
    @orderId int = null,
    @customerFileNo nvarchar(max) = null,
    @orderDate Date = null,
    @countyId nvarchar(max) = null,
    @clientName nvarchar(max) = null,
    @orderStatusId int = null,
    @vendorId int = null,
    @vendorETA nvarchar(max) = null,
    @dueDate DateTime = null,
    @guid nvarchar(max) = null,
    @orderStatusCsv nvarchar(max) = null,
    @researcherOrManagerId nvarchar(max) = null,
    @vendorsOnly bit = false,
    @researchersOnly bit = false,
    @includeDeletedRows bit = 0,
    @researcherId nvarchar(max) = null,
    @managerId nvarchar(max) = null,
    @allowViewArchive bit = 0
)
AS
BEGIN
    declare @timeZone varchar(25) = 'Eastern Standard Time' -- bake it in!
    SET NOCOUNT ON
        BEGIN
            Select top (Select ts.MaxRecords from TableSettings ts where ts.TableName = 'Order') 
                O.*,
                C.CountyName, 
                CU.DisplayName AS CustomerName, 
                OS.OrderStatus, 
                SR.Code AS SearchRequestCode, 
                IsNull(V.DisplayName, V.CompanyName) AS Vendor,
                V.EmailAddress, 
                O.CustomerDueDate AS DueDate,
                SP.StateProvinceName as StateProvince,
                U.UserName as Manager,
                M.UserName as Researcher
            FROM [dbo].[Order] O 
            left JOIN [dbo].[County] C ON O.CountyId = C.CountyId
            left JOIN [dbo].[Customer] CU ON O.CustomerId = CU.CustomerId
            left JOIN [dbo].[OrderStatus] OS ON O.OrderStatusId = OS.OrderStatusId
            left JOIN [dbo].[Vendor] V ON O.VendorId = V.VendorId
            left JOIN [dbo].[SearchRequest] SR ON O.SearchRequestId = SR.SearchRequestId
            left JOIN StateProvince SP ON SP.StateProvinceId = O.StateProvinceId
            left join AspNetUsers U on U.Id = O.ManagerId
            left join AspNetUsers M on M.Id = O.ResearcherId

            WHERE
            (@vendorsOnly = 0 or o.VendorId is not null) and
            (@researchersOnly = 0 or O.ResearcherId is not null) and
            (@orderId IS NULL OR O.OrderId  = @orderId) AND
            (@customerFileNo IS NULL OR O.CustomerFileNo LIKE '%' + trim(@customerFileNo) + '%') AND
            (@orderDate IS NULL OR Cast(O.OrderDateTime at time zone @timeZone as Date) = @orderDate) AND
            (@countyId IS NULL OR C.CountyId  = @countyId) AND
            (@clientName IS NULL OR CU.DisplayName LIKE '%' + trim(@clientName) + '%') AND
            (@orderStatusId IS NULL OR OS.OrderStatusId = @orderStatusId) AND
            (@vendorId IS NULL OR V.VendorId = @vendorId) AND
            (@vendorETA IS NULL OR O.VendorETA LIKE '%' + trim(@vendorETA) + '%') AND
            (@dueDate IS NULL OR O.CustomerDueDate = @dueDate) and
            (@guid IS NULL OR O.Guid = @guid) and
            (   --If @researcherOrManagerId is not null, then ensure that the order is assigned to the id as Manager or Researcher
                @researcherOrManagerId IS NULL or 
                (
                    O.ResearcherId = @researcherOrManagerId or 
                    O.ManagerId = @researcherOrManagerId or 
                    -- if @allowViewArchive is true, then allow to view if the Order in a status of Client Delivered.
                    (@allowViewArchive = 1 and O.OrderStatusId = 4) ) 
                
            ) and
            (@researcherId is null or O.ResearcherId = @researcherId) and
            (@managerId is null or O.ManagerId = @managerId) and
            (@orderStatusCsv IS NULL or O.OrderStatusId in (Select value from STRING_SPLIT(@orderStatusCsv, ','))) and
            (@includeDeletedRows = 1 or O.DeletedDate is null)
    
            --order by OrderId -- For testing only       
            ORDER BY OS.SortOrder, O.OrderId DESC
        end
END
sql-server azure-sql-database
1个回答
2
投票

就像评论中提到的那样,“修复”此问题的最简单方法是在语句末尾添加

OPTION (RECOMPILE)
,在这种情况下,这将位于
ORDER BY
之后:

...
            ORDER BY OS.SortOrder, O.OrderId DESC
            OPTION (RECOMPILE);
        END
END
然而,正如前面提到的,如果这个 SP 经常运行,或者生成计划本身成本很高,那么使用动态方法会更好,但这是一个更复杂的解决方案。根据您的查询,我相信以下是正确的实现,但我无法对此进行测试。您需要使用您的“最好的朋友”来帮助您:

ALTER PROCEDURE [dbo].[SearchOrderInfos_v21] (@orderId int = NULL, @customerFileNo nvarchar(MAX) = NULL, @orderDate date = NULL, @countyId nvarchar(MAX) = NULL, @clientName nvarchar(MAX) = NULL, @orderStatusId int = NULL, @vendorId int = NULL, @vendorETA nvarchar(MAX) = NULL, @dueDate datetime = NULL, @guid nvarchar(MAX) = NULL, @orderStatusCsv nvarchar(MAX) = NULL, @researcherOrManagerId nvarchar(MAX) = NULL, @vendorsOnly bit = false, @researchersOnly bit = false, @includeDeletedRows bit = 0, @researcherId nvarchar(MAX) = NULL, @managerId nvarchar(MAX) = NULL, @allowViewArchive bit = 0) AS BEGIN SET NOCOUNT ON; DECLARE @timeZone varchar(25) = 'Eastern Standard Time'; -- bake it in! DECLARE @SQL nvarchar(MAX), @CRLF nchar(2) = NCHAR(13) + NCHAR(10); SET @SQL = N'SELECT TOP (SELECT ts.MaxRecords FROM TableSettings ts WHERE ts.TableName = ''Order'')' + @CRLF + N' O.*,' + @CRLF + N' C.CountyName,' + @CRLF + N' CU.DisplayName AS CustomerName,' + @CRLF + N' OS.OrderStatus,' + @CRLF + N' SR.Code AS SearchRequestCode,' + @CRLF + N' ISNULL(V.DisplayName, V.CompanyName) AS Vendor,' + @CRLF + N' V.EmailAddress,' + @CRLF + N' O.CustomerDueDate AS DueDate,' + @CRLF + N' SP.StateProvinceName AS StateProvince,' + @CRLF + N' U.UserName AS Manager,' + @CRLF + N' M.UserName AS Researcher' + @CRLF + N'FROM [dbo].[Order] O' + @CRLF + N' LEFT JOIN [dbo].[County] C ON O.CountyId = C.CountyId' + @CRLF + N' LEFT JOIN [dbo].[Customer] CU ON O.CustomerId = CU.CustomerId' + @CRLF + N' LEFT JOIN [dbo].[OrderStatus] OS ON O.OrderStatusId = OS.OrderStatusId' + @CRLF + N' LEFT JOIN [dbo].[Vendor] V ON O.VendorId = V.VendorId' + @CRLF + N' LEFT JOIN [dbo].[SearchRequest] SR ON O.SearchRequestId = SR.SearchRequestId' + @CRLF + N' LEFT JOIN StateProvince SP ON SP.StateProvinceId = O.StateProvinceId' + @CRLF + N' LEFT JOIN AspNetUsers U ON U.Id = O.ManagerId' + @CRLF + N' LEFT JOIN AspNetUsers M ON M.Id = O.ResearcherId' + @CRLF + N'WHERE O.OrderStatusId = 4' + @CRLF + --This appears to be the only clause that doesn't reference a parameter, so putting it at the start as this makes things far easier CONCAT(CASE WHEN @vendorsOnly = 1 THEN N' AND O.VendorId IS NOT NULL' + @CRLF END, CASE WHEN @researchersOnly = 1 THEN N' AND O.ResearcherId IS NOT NULL' + @CRLF END, CASE WHEN @orderId IS NOT NULL THEN N' AND O.OrderId = @orderId' + @CRLF END, CASE WHEN @customerFileNo IS NOT NULL THEN N' AND O.CustomerFileNo LIKE ''%'' + trim(@customerFileNo) + ''%''' + @CRLF END, CASE WHEN @orderDate IS NOT NULL THEN N' AND CAST(O.OrderDateTime AT TIME ZONE @timeZone AS Date) = @orderDate' + @CRLF END, --Warning, this isn't going to be SARGable, --you should do use AT TIME ZONE on @orderDate, not O.OrderDateTime CASE WHEN @countyId IS NOT NULL THEN N' AND C.CountyId = @countyId' + @CRLF END, CASE WHEN @clientName IS NOT NULL THEN N' AND CU.DisplayName LIKE ''%'' + TRIM(@clientName) + ''%''' + @CRLF END, CASE WHEN @orderStatusId IS NOT NULL THEN N' AND OS.OrderStatusId = @orderStatusId' + @CRLF END, CASE WHEN @vendorId IS NOT NULL THEN N' AND V.VendorId = @vendorId' + @CRLF END, CASE WHEN @vendorETA IS NOT NULL THEN N' AND O.VendorETA LIKE ''%'' + TRIM(@vendorETA) + ''%''' + @CRLF END, CASE WHEN @dueDate IS NOT NULL THEN N' AND O.CustomerDueDate = @dueDate' + @CRLF END, CASE WHEN @guid IS NOT NULL THEN N' AND O.Guid = @guid' + @CRLF END, CASE WHEN @researcherOrManagerId IS NOT NULL AND @allowViewArchive = 0 THEN N' AND (O.ResearcherId = @researcherOrManagerId OR O.ManagerId = @researcherOrManagerId)' + @CRLF END, --I think the WHEN is correct CASE WHEN @researcherId IS NOT NULL THEN N' AND O.ResearcherId = @researcherId' + @CRLF END, CASE WHEN @managerId IS NOT NULL THEN N' AND O.ManagerId = @managerId' + @CRLF END, CASE WHEN @orderStatusCsv IS NOT NULL THEN N' AND O.OrderStatusId IN (SELECT value FROM STRING_SPLIT(@orderStatusCsv, '',''))' + @CRLF END, CASE WHEN @includeDeletedRows = 0 THEN N' AND O.DeletedDate IS NULL' + @CRLF END) + N'ORDER BY OS.SortOrder,' + @CRLF + N' O.OrderId DESC;'; --PRINT @SQL; --Your debugging best friend DECLARE @Params nvarchar(MAX) = N'@orderId int, @customerFileNo nvarchar(MAX), @orderDate date, @countyId nvarchar(MAX), @clientName nvarchar(MAX), @orderStatusId int, @vendorId int, @vendorETA nvarchar(MAX), @dueDate datetime, @guid nvarchar(MAX), @orderStatusCsv nvarchar(MAX), @researcherOrManagerId nvarchar(MAX), @vendorsOnly bit, @researchersOnly bit, @includeDeletedRows bit, @researcherId nvarchar(MAX), @managerId nvarchar(MAX), @allowViewArchive bit'; EXEC sys.sp_executesql @SQL, @Params, @orderId, @customerFileNo, @orderDate, @countyId, @clientName, @orderStatusId, @vendorId, @vendorETA, @dueDate, @guid, @orderStatusCsv, @researcherOrManagerId, @vendorsOnly, @researchersOnly, @includeDeletedRows, @researcherId, @managerId, @allowViewArchive; END;
    
© www.soinside.com 2019 - 2024. All rights reserved.