改进存储过程中的完整 sql 查询

Improving complete sql query in stored proc

我有一个存储过程,用于填充仪表板,如 WPF 中的网格。随着时间的推移,我已经改进了查询,老实说,性能可能会更好。我现在不知道有什么好的方法可以改进查询,在这种情况下我可以在执行时获得性能。

如有任何帮助,我们将不胜感激。下面是存储过程:

    ALTER PROCEDURE [dbo].[spGetDashboardMainNew]
    AS
         BEGIN
             SET NOCOUNT ON;
             SELECT JC.job_number AS SalesOrder,
                    dbo.cust.Company AS Customer,
                    JC.Plan_ship_date AS PlannedShipDate,
                    JC.latest_ship_date AS LatestShipDate,
                    JC.planned_fab_complete AS PlanFabComplete,
                    JC.fldPromisedDate AS CommittedShipDate,
                    JC.Materials_of_Construction AS MatofCons,
                    JC.Fab_rating AS FabRating,
                    JC.ass_rating AS [Assembly Rating],
                    JC.shipped AS OpenShipped,
                    JC.electrical_status,
                    JC.dp_credit_status,
                    JC.fldCustRequestDate,
                    JC.commercial_terms,
                    JC.Approved_by,
                    fldLiquidatedDamages,
                    fldLiquidatedDamagesDesc,
                    CASE
                        WHEN hot_list = '0'
                             OR hot_list = 'No'
                        THEN 'No'
                        ELSE 'Yes'
                    END AS HotList,
                    CASE
                        WHEN JC.latest_ship_date IS NOT NULL
                             AND JC.fldPromisedDate IS NULL
                        THEN JC.latest_ship_date
                        ELSE JC.fldPromisedDate
                    END AS CommitDate,
                    CASE
                        WHEN dp_credit_status = 1
                             AND commercial_terms = 'Complete'
                        THEN 1
                        ELSE 0
                    END AS TermsMet,
                    CASE
                        WHEN JC.fldPromisedDate IS NULL
                        THEN 1
                        ELSE 0
                    END AS SortByDate,
                    dbo.tblShippingInfo.fldShipmentRequestID,
                    CASE
                        WHEN JC.fleInspectionType <> 'None'
                        THEN JC.fleInspectionType
                        ELSE NULL
                    END AS InspectionType,
                    JC.fldInspectionNotes,
                 JC.DueDateExtraTime,
                    CASE
                        WHEN JC.fleInspectionType <> 'None'
                             AND JC.fleInspectionType IS NOT NULL
                        THEN 1
                        ELSE 0
                    END AS InspectionDesc,
                    advanced_buying,
                    SortedOrder,
                    SalesOrderStatusGroup,
                    SalesOrderStatus,
                    SpecialOpList,
                    OperationDueBy,
                    test,
                    wpStatus,
                    SpecialOpsCount,
                    InChangeOrder,
                    RequiresSpecialPaint
             FROM dbo.cust
                  INNER JOIN dbo.Job_Control JC ON dbo.cust.CUST# = JC.Cust#
                  LEFT OUTER JOIN dbo.tblShippingInfo ON JC.job_number = dbo.tblShippingInfo.fldJobNumberID
                  OUTER APPLY
             (
                 SELECT dbo.udfGetManStatusFullSortOrder(JC.job_number) AS SortedOrder
             ) acolumn
                  OUTER APPLY
             (
                 SELECT ISNULL(dbo.udfGetManStatusBySOGroupNo(JC.job_number, 1), dbo.udfGetManStatusNew(JC.job_number)) AS SalesOrderStatusGroup
             ) bcolumn
                  OUTER APPLY
             (
                 SELECT ISNULL(dbo.udfGetManStatusBySOGroupNo(JC.job_number, 1), dbo.udfGetManStatusNew(JC.job_number)) AS SalesOrderStatus
             ) ccolumn
                  OUTER APPLY
             (
                 SELECT dbo.udfGetSpecialOperations(JC.job_number) AS SpecialOpList
             ) dcolumn
                  OUTER APPLY
             (
                 SELECT CASE
                            WHEN dbo.udfGetManStatusNew(JC.job_number) LIKE '%Approval%' OR dbo.udfGetManStatusNew(JC.job_number) LIKE '%Re-Approval%'
                            THEN JC.Approval_Due
                            ELSE dbo.udfGetDueByPerOperation(JC.job_number, 1)
                        END AS OperationDueBy
             ) ecolumn
                  OUTER APPLY
             (
                 SELECT dbo.udfGetGroupCount(JC.job_number) AS test
             ) fcolumn
                  OUTER APPLY
             (
                 SELECT dbo.udfGetOpenWorkPackagesByDesigner(JC.job_number) AS wpStatus
             ) gcolumn
                  OUTER APPLY
             (
                 SELECT dbo.udfGetSpecialOpsCount(JC.job_number) AS SpecialOpsCount
             ) hcolumn
                  OUTER APPLY
             (
                 SELECT dbo.udfIsActiveChangeOrder(JC.job_number) AS InChangeOrder
             ) icolumn
                  OUTER APPLY
             (
                 SELECT dbo.udfRequiresSpecialColor(JC.job_number) AS RequiresSpecialPaint
             ) jcolumn
             WHERE(JC.shipped = 'Open')
                  OR (JC.shipped = 'Hold');
         END;

以下是调用的外部应用函数:

        ALTER FUNCTION [dbo].[udfGetManStatusFullSortOrder] 
    (
        @SalesOrderNumber int
    )
    RETURNS INT
    AS
    BEGIN
        DECLARE @shipped nvarchar(50);
        DECLARE @eng_complete datetime;
        DECLARE @Drawing_Info nvarchar(50);
        DECLARE @Release_to_engineering datetime;
        DECLARE @Dwg_Sent datetime;
        DECLARE @Approval_done datetime;
        DECLARE @Detail bit;
        DECLARE @Release_to_shop datetime;
        DECLARE @OperationCount int;
        DECLARE @SortOrder INT;
        DECLARE @p as INT = 1;
        DECLARE @NonOpSortOrder INT;

        SET @NonOpSortOrder = dbo.udfIsOnHoldSort(@SalesOrderNumber)

        IF @NonOpSortOrder > 0 
            RETURN @nonopsortorder

            SELECT @SortOrder = (SELECT TOP(@p) dbo.ShopRouting.DashboardSortOrder
                FROM    dbo.Product INNER JOIN
                      dbo.ProductMfgGrouping ON dbo.Product.Record_no = dbo.ProductMfgGrouping.Record_no INNER JOIN
                      dbo.MfgGrouping ON dbo.ProductMfgGrouping.MfgGroupingID = dbo.MfgGrouping.MfgGroupingID INNER JOIN
                      dbo.GroupOperations ON dbo.MfgGrouping.MfgGroupingID = dbo.GroupOperations.MfgGroupingID INNER JOIN
                      dbo.ShopRouting ON dbo.GroupOperations.ShopRoutingID = dbo.ShopRouting.ShopRoutingID
                WHERE  (dbo.Product.Job_number = @SalesOrderNumber) AND (dbo.MfgGrouping.GroupingNumber = 1) AND (dbo.GroupOperations.Completed IS NULL)
                GROUP BY dbo.ShopRouting.Description, dbo.GroupOperations.NewSortOrder, dbo.ShopRouting.DashboardSortOrder
                ORDER BY dbo.GroupOperations.NewSortOrder);

        IF @SortOrder IS NOT NULL
                RETURN @SortOrder

        SELECT
            @shipped = shipped,
            @eng_complete = eng_complete,
            @Drawing_Info = Drawing_Info,
            @Release_to_engineering = Release_to_engineering,
            @Dwg_Sent = Dwg_Sent,
            @Approval_done = Approval_done,
            @Detail = Detail,
            @Release_to_shop = Release_to_shop

        FROM
            job_control

        WHERE
            job_number = @SalesOrderNumber

        BEGIN
            SET @OperationCount = flex2ksql.dbo.udfGetGroupOperationsCount(@SalesOrderNumber);
        END

        IF (@shipped = 'Hold')
            RETURN 33

        IF (@eng_complete IS NULL)
            BEGIN
                 IF (@Release_to_engineering IS NULL)

                    RETURN 32

                IF (@Drawing_Info = 'Approval' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Dwg_Sent IS NOT NULL AND
                    @Approval_done IS NULL)

                    RETURN 28

                IF (@Drawing_Info = 'Re-Approval' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Dwg_Sent IS NOT NULL AND
                    @Approval_done IS NULL)

                    RETURN 29

                IF (@Drawing_Info = 'Approval' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Dwg_Sent IS NOT NULL AND
                    @Detail = 1 AND
                    @Approval_done IS NOT NULL)

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 27
                    ELSE
                        RETURN 26

                IF (@Drawing_Info = 'Approval' AND 
                    @Release_to_engineering IS NOT NULL)

                    RETURN 30

                IF (@Drawing_Info = 'Re-Approval' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Dwg_Sent IS NULL AND
                    @Approval_done IS NULL)

                    RETURN 31

                IF (@Drawing_Info = 'Certified' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Detail = 1)

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 27
                    ELSE
                        RETURN 26

                IF (@Drawing_Info = 'No Drawing Required' AND
                    @Release_to_engineering IS NOT NULL)

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 27
                    ELSE
                        RETURN 26

                IF (@Drawing_Info = 'Certified')

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 27
                    ELSE
                        RETURN 26

                IF (@Drawing_Info = 'Sales Drawing' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Release_to_shop IS NOT NULL)

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 27
                    ELSE
                        RETURN 26
                ELSE
                    RETURN 27

            END
        ELSE
            BEGIN
                RETURN 27
            END
        RETURN 99



        ALTER FUNCTION [dbo].[udfGetManStatusBySOGroupNo] 
    (
        @SalesOrder int, @GroupNo int
    )
    RETURNS varchar(100)
    AS
    BEGIN

        DECLARE @ManStatus varchar(100);
        DECLARE @p as INT = 1;
        DECLARE @NonOpSortOrder INT;

        SET @NonOpSortOrder = dbo.udfIsOnHoldSort(@SalesOrder)

        IF @NonOpSortOrder > 0 
            RETURN 'Hold'
        ELSE

                SELECT @ManStatus = (SELECT TOP(@p) dbo.ShopRouting.Description
                FROM dbo.Product INNER JOIN
                      dbo.ProductMfgGrouping ON dbo.Product.Record_no = dbo.ProductMfgGrouping.Record_no INNER JOIN
                      dbo.MfgGrouping ON dbo.ProductMfgGrouping.MfgGroupingID = dbo.MfgGrouping.MfgGroupingID INNER JOIN
                      dbo.GroupOperations ON dbo.MfgGrouping.MfgGroupingID = dbo.GroupOperations.MfgGroupingID INNER JOIN
                      dbo.ShopRouting ON dbo.GroupOperations.ShopRoutingID = dbo.ShopRouting.ShopRoutingID
                WHERE  (dbo.Product.Job_number = @SalesOrder) AND (dbo.MfgGrouping.GroupingNumber = @GroupNo) AND (dbo.GroupOperations.Completed is null)
                GROUP BY dbo.ShopRouting.Description, dbo.Product.Model_NO, dbo.GroupOperations.NewSortOrder
                ORDER BY dbo.GroupOperations.NewSortOrder);

                RETURN @ManStatus


        ALTER FUNCTION [dbo].[udfGetManStatusNew] 
    (
        @SalesOrderNumber int
    )
    RETURNS nvarchar(50)
    AS
    BEGIN
        DECLARE @shipped nvarchar(50);
        DECLARE @eng_complete datetime;
        DECLARE @Drawing_Info nvarchar(50);
        DECLARE @Release_to_engineering datetime;
        DECLARE @Dwg_Sent datetime;
        DECLARE @Approval_done datetime;
        DECLARE @Detail bit;
        DECLARE @Release_to_shop datetime;
        DECLARE @OperationCount int;

        SELECT
            @shipped = shipped,
            @eng_complete = eng_complete,
            @Drawing_Info = Drawing_Info,
            @Release_to_engineering = Release_to_engineering,
            @Dwg_Sent = Dwg_Sent,
            @Approval_done = Approval_done,
            @Detail = Detail,
            @Release_to_shop = Release_to_shop

        FROM
            job_control

        WHERE
            job_number = @SalesOrderNumber

        BEGIN
            SET @OperationCount = flex2ksql.dbo.udfGetGroupOperationsCount(@SalesOrderNumber);
        END

        IF (@shipped = 'Hold')
            RETURN 'Hold'

        IF (@eng_complete IS NULL)
            BEGIN
              IF (@Release_to_engineering IS NULL)

                    RETURN 'Not Released'

                IF (@Drawing_Info = 'Approval' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Dwg_Sent IS NOT NULL AND
                    @Approval_done IS NULL)

                    RETURN 'Approval Out'

                IF (@Drawing_Info = 'Re-Approval' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Dwg_Sent IS NOT NULL AND
                    @Approval_done IS NULL)

                    RETURN 'Re-Approval Out'

                IF (@Drawing_Info = 'Approval' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Dwg_Sent IS NOT NULL AND
                    @Detail = 1 AND
                    @Approval_done IS NOT NULL)

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 'Routing'
                    ELSE
                        RETURN 'Detail'

                IF (@Drawing_Info = 'Approval' AND 
                    @Release_to_engineering IS NOT NULL)

                    RETURN 'Approval To Be Done'

                IF (@Drawing_Info = 'Re-Approval' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Dwg_Sent IS NULL AND
                    @Approval_done IS NULL)

                    RETURN 'Re-Approval To Be Done'
                IF (@Drawing_Info = 'Certified' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Detail = 1)

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 'Routing'
                    ELSE
                        RETURN 'Detail'

                IF (@Drawing_Info = 'No Drawing Required' AND
                    @Release_to_engineering IS NOT NULL)

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 'Routing'
                    ELSE
                        RETURN 'Detail'

                IF (@Drawing_Info = 'Certified')

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 'Routing'
                    ELSE
                        RETURN 'Detail'

                IF (@Drawing_Info = 'Sales Drawing' AND
                    @Release_to_engineering IS NOT NULL AND
                    @Release_to_shop IS NOT NULL)

                    If (@OperationCount) = 0 OR (@OperationCount IS NULL)
                        RETURN 'Routing'
                    ELSE
                        RETURN 'Detail'
                ELSE
                    RETURN 'Routing'

            END
        ELSE
            BEGIN
                RETURN 'Routing'
            END
        RETURN NULL


ALTER FUNCTION [dbo].[udfGetSpecialOperations] 
(
@SalesOrder int
)
RETURNS nvarchar(MAX)
AS
BEGIN   
DECLARE @returnVal nvarchar(MAX)

SELECT @returnVal = Stuff((SELECT N', ' + dbo.ShopRouting.Description FROM dbo.Product INNER JOIN
        dbo.ProductMfgGrouping ON dbo.Product.Record_no = dbo.ProductMfgGrouping.Record_no INNER JOIN
        dbo.MfgGrouping ON dbo.ProductMfgGrouping.MfgGroupingID = dbo.MfgGrouping.MfgGroupingID INNER JOIN
        dbo.GroupOperations ON dbo.MfgGrouping.MfgGroupingID = dbo.GroupOperations.MfgGroupingID INNER JOIN
        dbo.ShopRouting ON dbo.GroupOperations.ShopRoutingID = dbo.ShopRouting.ShopRoutingID                         
    WHERE (dbo.ShopRouting.Standard = 0) AND (dbo.Product.Job_number = @SalesOrder)
    GROUP BY dbo.ShopRouting.Description, dbo.ShopRouting.DashboardSortOrder
    ORDER BY dbo.ShopRouting.DashboardSortOrder DESC 
    FOR XML PATH(''),TYPE).value('text()[1]','nvarchar(max)'),1,2,N'')

RETURN @returnVal


        ALTER FUNCTION [dbo].[udfGetDueByPerOperation] 
    (
        @SalesOrder int, @GroupNo int
    )
    RETURNS varchar(100)
    AS
    BEGIN
        DECLARE @ManStatus varchar(100);
        DECLARE @p as INT = 1;

        SELECT @ManStatus = (SELECT TOP(@p) dbo.GroupOperations.StartBy
    FROM     dbo.Product INNER JOIN
                      dbo.ProductMfgGrouping ON dbo.Product.Record_no = dbo.ProductMfgGrouping.Record_no INNER JOIN
                      dbo.MfgGrouping ON dbo.ProductMfgGrouping.MfgGroupingID = dbo.MfgGrouping.MfgGroupingID INNER JOIN
                      dbo.GroupOperations ON dbo.MfgGrouping.MfgGroupingID = dbo.GroupOperations.MfgGroupingID INNER JOIN
                      dbo.ShopRouting ON dbo.GroupOperations.ShopRoutingID = dbo.ShopRouting.ShopRoutingID
    WHERE  (dbo.Product.Job_number = @SalesOrder) AND (dbo.MfgGrouping.GroupingNumber = @GroupNo) AND (dbo.GroupOperations.Completed IS NULL) AND (dbo.GroupOperations.Active = 0)
    GROUP BY dbo.ShopRouting.[Description], dbo.Product.Model_NO, dbo.GroupOperations.NewSortOrder, dbo.GroupOperations.StartBy
    ORDER BY dbo.GroupOperations.NewSortOrder);

        RETURN @ManStatus


        ALTER FUNCTION [dbo].[udfGetGroupCount] 
    (
        @SalesOrder int
    )
    RETURNS varchar(MAX)
    AS
    BEGIN
        DECLARE @result varchar(MAX)
        DECLARE @result2 varchar(MAX)

    SET @result = ''

    SELECT @result = @result + Description + ': ' + Cast(StatusCount as varchar(5)) + ', ' FROM vwLineItemGroupStatusCounts WHERE Job_number = @SalesOrder
    ORDER BY vwLineItemGroupStatusCounts.NewSortOrder

    SELECT @result2 = substring(@result, 0, len(@result)) --trim extra "," at end

        -- Return the result of the function
        RETURN @result2


        ALTER FUNCTION [dbo].[udfGetOpenWorkPackagesByDesigner] 
    (
        @SalesOrder int
    )
    RETURNS varchar(MAX)
    AS
    BEGIN
        DECLARE @result varchar(MAX)
        DECLARE @result2 varchar(MAX)

    SET @result = ''

    --SELECT @result = @result + Fldusername + ': ' + Cast(StatusCount as varchar(5)) + ', ' FROM vwOpenWorkPackagesByDesigner WHERE Job_number = @SalesOrder
    SELECT @result = @result + eng_difficulty_category + ' ' FROM vwOpenWorkPackagesEngRating WHERE job_number = @SalesOrder
    SELECT @result = @result + Fldusername + ' ' + Cast(coalesce(fldPriority,'none') as varchar(5)) + ' ' + cast(PercentComplete as varchar(10)) + '% ' + fldTaskCategoryAbbr + '; ' FROM vwOpenWorkPackagesByDesigner WHERE fldSalesOrder = @SalesOrder

    SELECT @result2 = substring(@result, 0, len(@result)) --trim extra "," at end

        -- Return the result of the function
        RETURN @result2


        ALTER FUNCTION [dbo].[udfGetSpecialOpsCount] 
    (
        @SalesOrder int
    )
    RETURNS int
    AS
    BEGIN

        DECLARE @returnVal int;

        SELECT @returnVal = COUNT(dbo.GroupOperations.GroupOperationID)
    FROM     dbo.Product INNER JOIN
                      dbo.ProductMfgGrouping ON dbo.Product.Record_no = dbo.ProductMfgGrouping.Record_no INNER JOIN
                      dbo.MfgGrouping ON dbo.ProductMfgGrouping.MfgGroupingID = dbo.MfgGrouping.MfgGroupingID INNER JOIN
                      dbo.GroupOperations ON dbo.MfgGrouping.MfgGroupingID = dbo.GroupOperations.MfgGroupingID INNER JOIN
                      dbo.ShopRouting ON dbo.GroupOperations.ShopRoutingID = dbo.ShopRouting.ShopRoutingID
    WHERE  (dbo.ShopRouting.Standard = 0) AND (dbo.Product.Job_number = @SalesOrder) AND (dbo.ShopRouting.ShopRoutingID NOT IN (15,16))

        If (@returnVal) = 0 OR (@returnVal IS NULL)
            SET @returnVal = 0;
        Return @returnVal;


        ALTER FUNCTION [dbo].[udfIsActiveChangeOrder] (@SalesOrder numeric(18, 0))

    RETURNS Bit

    AS

    BEGIN

        DECLARE @isCO Bit
        DECLARE @SOFound as numeric(18)

        SET @SOFound = 0

            BEGIN
            SELECT
                @SOFound = fldSalesOrder        
            FROM
                [flex2kSQL].[dbo].[vwChangeOrdersAllActive]
            WHERE
                fldSalesOrder = @SalesOrder
            END


            if @SOFound =0
                SET @isCO=0
            else
                SET @isCO=1


        RETURN @isCO


        ALTER FUNCTION [dbo].[udfRequiresSpecialColor] 
    (
        @SalesOrder INT
    )
    RETURNS INT
    AS
    BEGIN
        DECLARE @ReturnVal int;

        IF EXISTS (SELECT 1
            FROM dbo.MfgGrouping INNER JOIN
                dbo.ProductMfgGrouping ON dbo.MfgGrouping.MfgGroupingID = dbo.ProductMfgGrouping.MfgGroupingID INNER JOIN
                dbo.Product ON dbo.ProductMfgGrouping.Record_no = dbo.Product.Record_no INNER JOIN
                dbo.GroupOperations ON dbo.MfgGrouping.MfgGroupingID = dbo.GroupOperations.MfgGroupingID
            WHERE (dbo.Product.Job_number = @SalesOrder) AND (dbo.GroupOperations.ShopRoutingID IN (23, 24, 25)))
        BEGIN
            SET @ReturnVal = 1
        END
        ELSE
        BEGIN
            SET @ReturnVal = 0
        END

        Return @ReturnVal;

下面是执行存储过程的一行示例数据(删除了除测试顺序外的每一行):

SalesOrder Customer PlannedShipDate LatestShipDate PlanFabComplete CommittedShipDate MatofCons FabRating Assembly Rating OpenShipped electrical_status dp_credit_status fldCustRequestDate commercial_terms Approved_by fldLiquidatedDamages fldLiquidatedDamagesDesc HotList CommitDate TermsMet SortByDate fldShipmentRequestID InspectionType fldInspectionNotes DueDateExtraTime InspectionDesc advanced_buying SortedOrder SalesOrderStatusGroup SalesOrderStatus SpecialOpList OperationDueBy test wpStatus SpecialOpsCount InChangeOrder RequiresSpecialPaint 76506 Test Customer 6/16/2020 12/31/2020 NULL 11/28/2016 SS C C Open In Test 0 9/29/2016 NULL Premount/Prewire 0 NULL Yes 11/28/2016 0 0 NULL NULL NULL 7 0 Complete 26 Detail Detail NULL 6/2/2020 Detail: 6 NULL 0 0 0

最后,下面的屏幕截图显示了数据在主窗体上的显示和组织方式。

--编辑-- 将调用的函数更改为内联 table 函数后的最新查询执行计划。 Latest Query Execution Plan

查询优化与其说是一门科学,不如说是一门艺术。

值得注意的是,您使用的许多函数都是以命令式风格编写的。作为起点,我将研究是否可以将针对函数调用的 OUTER APPLY 重写为针对视图的 LEFT JOIN(或者内联编写,如果函数对于此报告是唯一的,或者针对预定义的视图)。

所以例如函数udfRequiresSpecialColor可以重写为:

SELECT
    dbo.Product.Job_number

FROM 
    dbo.MfgGrouping 

INNER JOIN
    dbo.ProductMfgGrouping 
    ON dbo.MfgGrouping.MfgGroupingID = dbo.ProductMfgGrouping.MfgGroupingID 

INNER JOIN
    dbo.Product 
    ON dbo.ProductMfgGrouping.Record_no = dbo.Product.Record_no 

INNER JOIN 
    dbo.GroupOperations 
    ON dbo.MfgGrouping.MfgGroupingID = dbo.GroupOperations.MfgGroupingID
    AND (dbo.GroupOperations.ShopRoutingID IN (23, 24, 25))

这个returns一个table包含所有 Job_number需要特殊颜色的,可以加入。

然后,在主查询中,替换下面的OUTER APPLY:

SELECT
    ...
    RequiresSpecialPaint
    ...
OUTER APPLY
(
    SELECT dbo.udfRequiresSpecialColor(JC.job_number) AS RequiresSpecialPaint
) AS jcolumn

...并在主查询中替换为 LEFT JOIN:

SELECT
    ...
    IIF(jobs_requiring_special_paint.job_number IS NOT NULL, 1, 0) AS RequiresSpecialPaint
    ...

LEFT JOIN
    (
        [here you can either a call to the new udfRequiresSpecialColor defined as a database view or an inlinable function, or simply include the code above as a subquery]
    ) AS jobs_requiring_special_paint
    ON (JC.job_number = jobs_requiring_special_paint.job_number)

通过左连接到这个 table,那些有特殊油漆的工作会有一个 job_number,而那些没有特殊油漆的工作会有一个 NULL 值 - 然后转换select 语句中的 0/1 标志,而不是子函数的一部分。

您了解了大致情况,并且可以看到处理获取特殊油漆数据的整个查询部分现在以纯粹基于集合的方式处理。

删除命令式代码为查询优化器提供了更大的自由度,可以更有效地分析和重组查询,同时保留您定义的逻辑。

您最终可能还会发现,当前各种不同的外部应用查询可以压缩为相关 table 关系的单个通用定义(或更少的通用定义),并应用必要的过滤在 ON 子句中。

例如,udfGetDueByPerOperation 似乎与 udfRequiresSpecialColor 命中相同的 table(那些 table 是 MfgGrouping, ProductMfgGrouping, Product, GroupOperations),并且在相同的基本关系(同一键上的内部连接),但最后加上 ShopRouting

因此可以定义公共代码(作为数据库视图或内联函数,或作为公共 table expression/WITH 子句),以及在最后的 JOIN 子句中实现的过滤反对共同 table。这可能会使代码更短并且更容易理解和维护,并且它可能再次帮助查询引擎优化其计划。如果需要,我可以详细讨论如何应用这种方法。

我注意到有些函数(例如 udfGetManStatusNew)具有相对复杂的决策逻辑,可能难以在保持清晰性和正确性的同时简化为基于表达式的计算。

尽管如此,即使作为没有从函数中完全消除命令式代码的部分解决方案,如果以给定 的方式重写这样的函数,它也可能更有效table 所有 job_numbers,点击 job_control table 一次获取所有相关工作的详细信息,使用 OUTER APPLY 点击 udfGetGroupOperationsCount 函数(或使用上述技术将 OUTER APPLY 减少到 LEFT JOIN)并将 @OperationCount 捕获为附加列(而不是分配给标量变量)。然后,一旦函数的 'ingredients' 以这种方式聚集到单个 table 中,then 遍历命令式代码以获得必要的计算每个作业的状态)。

我不想预先判断您将从这种通用方法中获得的结果,因为查询引擎可能是不可预测的table,但我怀疑它应该会提高性能,如果可以的话待改进。

如果您能了解查询的哪些部分对其性能不佳的贡献最大 - 假设它并非一律缓慢。

如果您需要更多指导,请提出。如果您对此感到满意,也请告诉我。

编辑: 更多信息,基于以下评论。

我刚刚意识到我误解了您 post 编辑的查询计划 - 因为函数是命令式的,它没有显示它们的查询计划,也不包括它们的成本。如果您将它们重写为可内联函数,post 另一个查询计划,它将开始显示查询计划和函数本身的全部成本(目前它们作为低成本常量扫描出现在查询计划中,没有进一步的计划信息)。

imperative/multi-statement 函数和可内联函数之间的区别如下所示(注意避免混淆,我在 SQL 服务器中有一个 DUAL table,以镜像 Oracle默认内置的功能):

CREATE FUNCTION [dbo].[fxn_imperative] 
(   
     @dummy     VARCHAR(1)
)
RETURNS INT
AS
BEGIN
    DECLARE @ReturnVal int;

    IF EXISTS (SELECT 1 FROM DUAL WHERE DUMMY = @dummy)
        BEGIN
            SET @ReturnVal = 1
        END
    ELSE
        BEGIN
            SET @ReturnVal = 0
        END

    Return @ReturnVal;
END
-- called like so: SELECT dbo.fxn_imperative('X') AS dummy_exists;


CREATE FUNCTION [dbo].[fxn_inline] 
(   
     @dummy     VARCHAR(1)
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT ISNULL( (SELECT 1 FROM DUAL WHERE DUMMY = @dummy), 0) AS dummy_exists
)
-- called like so: SELECT * FROM fxn_inline('X');

这些函数在功能上或多或少是等效的 - 但只有内联函数会出现在调用该函数的查询的查询计划中。

关于 LEFT JOIN 方法,在这种情况下,您没有将内部查询定义为函数 - 您将其定义为视图(或简单地将其内联写入子查询)并加入它(使用 job_number 作为连接条件,作为传递给原始函数的参数的替换)。作为我原来的一部分 post.

,我已经在上面粗略地展示了这种方法是如何应用的

如果我想了解您现有查询的处理方式(就调用外部应用的函数而言),我将按如下方式定义函数(我假设基础查询对于特殊涂料,仅 returns 每 job/salesorder 一行):

CREATE FUNCTION [dbo].[udfRequiresSpecialColor2] 
(   
     @SalesOrder INT
)
RETURNS TABLE 
AS
RETURN 
(

    WITH special_paint_subquery AS
    (
        SELECT
            dbo.Product.Job_number

        FROM 
            dbo.MfgGrouping 

        INNER JOIN
            dbo.ProductMfgGrouping 
            ON dbo.MfgGrouping.MfgGroupingID = dbo.ProductMfgGrouping.MfgGroupingID 

        INNER JOIN
            dbo.Product 
            ON dbo.ProductMfgGrouping.Record_no = dbo.Product.Record_no 

        INNER JOIN 
            dbo.GroupOperations 
            ON dbo.MfgGrouping.MfgGroupingID = dbo.GroupOperations.MfgGroupingID
            AND (dbo.GroupOperations.ShopRoutingID IN (23, 24, 25))

    )

    SELECT ISNULL( (SELECT 1 FROM special_paint_subquery WHERE dbo.Product.Job_number = @SalesOrder), 0) AS requires_special_paint
)

如果以这种方式编写函数,则在您获得主查询的计划时会显示该函数的计划。

该函数的调用方式与您当前调用 udfRequiresSpecialColor 的方式相同 - 通过将其与作为参数传递的 job_number 交叉应用。

一旦您以这种方式定义了函数,实际上将函数调用转换为左连接就变得微不足道了(WITH special_paint_subquery ... 部分进入视图定义,WHERE dbo.Product.Job_number = @SalesOrder 进入连接主查询中的条件,主查询的 SELECT 语句被修改为将 NULL 匹配转换为零,如我最初所示)。