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

Sql server 改进存储过程中的完整sql查询,sql-server,tsql,stored-procedures,sql-function,Sql Server,Tsql,Stored Procedures,Sql Function,我有一个存储过程,用于在WPF中填充类似于仪表板的网格。随着时间的推移,我对查询进行了改进,老实说,性能可能会更好。我现在的处境是,我不知道有什么好的方法来改进查询,以便在执行时获得性能 任何帮助都将不胜感激。下面是存储的进程: ALTER PROCEDURE [dbo].[spGetDashboardMainNew] AS BEGIN SET NOCOUNT ON; SELECT JC.job_number

我有一个存储过程,用于在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;
下面是执行存储过程的一行示例数据(除测试顺序外,每行都已删除):

销售订单客户计划发货日期最晚发货日期计划制造完整的委托发货日期马托夫康制造装配评级未发货电气状况dp信用状况FLD行业要求日期商业条款FLD批准FLD液化损坏FLD液化损坏SDSC热名单委托条款SMET SortByDatefldShipmentRequestID检查类型FLD检查注释到期额外时间检查描述高级采购分拣机销售订单状态组销售订单状态特殊清单操作到期测试wpStatus特殊清单更改订单要求特殊油漆 76506测试客户6/16/2020 12/31/2020 NULL 11/28/2016 SS C C在测试中打开0 2016年9月29日NULL预安装/预接线0 NULL是2016年11月28日0 NULL NULL 7 0完成26详细信息NULL 6/2/2020详细信息:6 NULL 0 0 0 0

最后,下面是一个屏幕截图,显示了如何在主窗体上显示和组织数据

--编辑-- 将调用的函数更改为内联表函数后的最新查询执行计划。
查询优化与其说是一门科学,不如说是一门艺术

突出的是,您使用的许多函数都是以命令式风格编写的。作为一个起点,我将研究针对函数调用的外部应用程序是否可以重写为针对视图的左连接(如果函数对此报表是唯一的,则可以内联编写,或者针对预定义视图编写)

例如,函数
udfrequeuirespecialcolor
可以重写为:

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))
这将返回一个表,其中包含所有需要特殊颜色的
工单编号
s,这些颜色可以与

然后,在主查询中,替换以下外部应用:

SELECT
    ...
    RequiresSpecialPaint
    ...
OUTER APPLY
(
    SELECT dbo.udfRequiresSpecialColor(JC.job_number) AS RequiresSpecialPaint
) AS jcolumn
…并在主查询中替换为左联接:

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)
通过左键连接到此表,具有特殊绘制的作业将有一个作业编号,而不具有特殊绘制的作业将有一个空值-然后在select语句中转换为0/1标志,而不是子函数的一部分

您得到了总体情况,您可以看到查询中处理获取特殊绘制数据的整个部分,现在是以纯粹基于集合的方式处理的

删除命令式代码为查询优化人员提供了更大的自由度来分析和重新组织查询,使其能够更高效地执行,同时保留您定义的逻辑

您还可能最终发现,当前各种独立的外部APPLY查询可以压缩为相关表关系的单个公共定义(或更少的公共定义),并在ON子句中应用必要的过滤

例如,
udfgetduebyperooperation
似乎与
udfrequeuirespecialcolor
(这些表是
MfgGrouping、ProductMfgGrouping、Product、GroupOperations
)所处的表相同,并且具有相同的基本关系(相同键上的内部联接),但在末尾加上了
ShopRouting

因此,可以定义公共代码(作为数据库视图或内联函数,或作为公共表表达式/WITH子句),以及针对公共表在最终JOIN子句中实现的过滤。这可能会使代码变得更短,更易于理解和维护,并且可能会再次帮助查询引擎优化其计划。如果需要的话,我可以更多地谈谈如何应用这种方法

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

尽管如此,即使是不完全从函数中删除命令性代码的部分解决方案,如果以这样的方式重写该函数,即为其提供一个包含所有作业编号的表,并点击一次
job\u控件
表以获取所有相关作业的详细信息,可能会更有效,使用外部应用为每个作业点击
udfGetGroupOperationsCount
函数(或使用上述技术减少对左联接的外部应用),并将
@OperationCount
捕获为附加列(而不是指定给标量变量)。然后,一旦以这种方式将函数的“成分”收集到一个表中,然后迭代命令式代码,为每个作业的状态导出必要的计算)

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

如果您能够了解查询的哪些部分是导致其性能差的最主要原因,那么这也可能会有所帮助——假设它的速度不是一致的慢

如果你需要更多的指导,就直接问吧。如果你对此感到高兴,也请告诉我

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

我刚刚意识到我误解了您发布的查询计划-因为函数是必需的,它没有显示它们的查询计划,也没有包括它们的成本。如果将它们重写为可内联函数,请发布另一个查询pl
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)
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');
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
)