我有一个存储过程,用于在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客户PlannedShipDate LatestShipDate PlanFabComplete CommittedShipDate MatofCons FabRating大会评分OpenShipped electrical_status dp_credit_status fldCustRequestDate commercial_terms Approved_by fldLiquidatedDamages fldLiquidatedDamagesDesc好友名单CommitDate TermsMet SortByDate fldShipmentRequestID InspectionType fldInspectionNotes DueDateExtraTime InspectionDesc advanced_buying SortedOrder SalesOrderStatusGroup SalesOrderStatus SpecialOpList OperationDueBy测试wpStatus SpecialOpsCount InChangeOrder RequiresSpecialPaint 76506测试客户6/16/2020 12/31/2020 NULL 11/28/2016 SS CC在测试中打开0 9/29/2016 NULL Premount / Prewire 0 NULL是11/28/2016 0 0 NULL NULL NULL 7 0完成26详细信息NULL 6/2/2020详细信息:6 NULL 0 0 0
最后,下面是一个屏幕截图,显示了如何在主窗体上显示和组织数据。
- 编辑 - 更改调用内联表函数的函数后的最新查询执行计划。 Latest Query Execution Plan
答案 0 :(得分:1)
查询优化更像是一种艺术形式而非科学。
最突出的是,您所使用的许多功能都是以命令式的方式编写的。作为一个起点,我将研究是否可以将针对函数调用的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))
这将返回一个包含所有 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)
通过左键连接到此表,将有一个job_number用于那些具有特殊绘制的作业,并且那些作业的NULL值将不会被转换为0/1标记。选择语句,而不是成为子函数的一部分。
您可以看到一般情况,您可以看到查询的整个部分处理获取特殊颜料数据,现在以纯粹基于集合的方式处理。
删除命令式代码使查询优化器能够以更高效的方式分析和重新组织查询,同时保留您定义的逻辑。
您最终还可以发现,当前各种单独的OUTER APPLY查询可以压缩为相关表关系的单个公共定义(或更少的通用定义),并在ON子句中应用必要的过滤。 / p>
例如,udfGetDueByPerOperation
似乎与udfRequiresSpecialColor
(那些表为MfgGrouping, ProductMfgGrouping, Product, GroupOperations
)和相同的基本关系(相同键上的INNER JOIN)在同一个表中,但是最后加ShopRouting
。
因此可以定义公共代码(作为数据库视图或内联函数,或作为公用表表达式/ WITH子句),以及在针对公用表的最终JOIN子句中实现的过滤。这可能会使代码更短,更容易理解和维护,并且它可能有助于查询引擎优化其计划。如果需要,我可以更多地讨论如何应用这种方法。
我注意到有一些函数,例如udfGetManStatusNew
,它们具有相对复杂的决策逻辑,这可能很难简化为基于表达式的计算,同时保持清晰度和正确性。
尽管如此,即使作为部分解决方案而没有从函数中完全消除命令性代码,如果以给定表在所有job_numbers中,为了所有相关作业的详细信息,点击job_control
表,使用OUTER APPLY命中每个作业的udfGetGroupOperationsCount
函数(或使用上述技术减少OUTER APPLY)到LEFT JOIN)并将@OperationCount
作为附加列捕获(而不是分配给标量变量)。然后,一旦&#39;成分&#39;函数已经以这种方式收集到一个表中,然后遍历命令式代码,以获得每个作业状态的必要计算。)
我不想预先判断您将从这种一般方法中获得的结果,因为查询引擎可能无法预测,但我怀疑它是否应该提高性能,如果可以改进的话。
如果您可以了解查询的哪些部分对其不良性能的贡献最大,也可能会有所帮助 - 假设它不是一直很慢。
如果您需要更多指导,请随时提出。如果你对此感到高兴,请告诉我。
修改强> 更多信息,基于以下评论。
我刚刚意识到我误解了您发布的查询计划 - 因为功能是必不可少的,它不会显示它们的查询计划,也不包括它们的成本。如果您将它们重写为无法使用的函数,请发布另一个查询计划,该计划将开始显示查询计划以及函数本身的全部成本(当前它们作为低成本常量扫描出现在查询计划中,没有进一步的计划信息)。
命令式/多语句函数和无法使用的函数之间的区别如下(注意为了避免混淆,我在SQL Server中有一个DUAL表,以镜像默认情况下内置的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)作为连接条件,作为已传递给原始函数的参数的替换。我已经大致展示了如何在上面应用这种方法作为原始帖子的一部分。
如果我想了解现有查询的处理方式(就调用OUTER APPLYed的函数而言),我将如下定义函数(我假设特殊绘制的基础查询只返回每个作业/销售订单的单行:
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匹配转换为零,如我最初所示。)