改进存储过程中的完整 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 匹配转换为零,如我最初所示)。
我有一个存储过程,用于填充仪表板,如 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 匹配转换为零,如我最初所示)。