-
Type:
Story
-
Status: Closed (View Workflow)
-
Resolution: Won't Develop
-
Affects Version/s: None
-
Fix Version/s: None
-
Component/s: None
-
Labels:None
-
SCRUM Team:Watchmen
-
Work Type Classification:Sustaining
Ryan Thomson (a SQL optimization consultant with Fourth) suggested this for this SQL statement:
Declare @tmp_Forecasts table ( ForecastID bigint primary key clustered ) Insert into @tmp_Forecasts Select ForecastId FROM ( SELECT ForecastId, ROW_NUMBER() OVER (PARTITION BY EntityId, BusinessDay ORDER BY GenerationDate DESC) RowNumber FROM Forecasting.Forecast WITH(NOLOCK) WHERE EntityId = @p0 And BusinessDay between CONVERT(date, @p1) AND CONVERT(date, @p2) ) f WHERE RowNumber = 1 SELECT i.InventoryItemId, CAST(CASE WHEN i.ServiceType IN ('' + @p3 + '') THEN 1 ELSE 0 END AS bit) AS Excluded, Sum(i.RawTransactionCount) AS RawTransactionCount, Sum(i.SystemTransactionCount) AS SystemTransactionCount, Sum(i.ManagerTransactionCount) AS ManagerTransactionCount, Sum(i.LastYearTransactionCount) AS LastYearTransactionCount FROM Forecasting.InventoryItemMetricDetail i WITH(NOLOCK) Inner Join @tmp_Forecasts Fore on ( i.ForecastId = Fore.ForecastId ) WHERE i.IntervalStart between @p1 AND @p2 GROUP BY i.InventoryItemId, CASE WHEN i.ServiceType IN ('' + @p3 + '') THEN 1 ELSE 0 END
And his explanation:
- For SQL to use a better estimation plan, pre-select the ForecastIDs into a temp table variable, instead of storing it as a comma separated value in a local variable.
i.e :
From :SELECT @IDList = COALESCE(@IDList +'','', '''') + CONVERT(varchar(20),ForecastId)
To :
Insert into @tmp_Forecasts Select ForecastID FROM Forecasting.Forecast F WITH(NOLOCK)
- Dynamic SQL : This query has a dynamic SQL statement within a Dynamic Statement ( 2 levels ). When joining tables and trying to use the Partition Keys / Indexes, try to remove concatenations & dynamic queries as much as possible. By pre-selecting the ForecastIDs into a temp table variable and joining with the Main Forecasting.InventoryItemMetricDetail table, SQL can better estimate the plan.
i.e :
From :FROM Forecasting.InventoryItemMetricDetail i WITH(NOLOCK) WHERE i.ForecastId IN ('' + @IDList + '') AND i.IntervalStart >= @p0 AND i.IntervalStart <= @p1
To :
FROM Forecasting.InventoryItemMetricDetail i WITH(NOLOCK) Inner Join @tmp_Forecasts Fore on ( i.ForecastId = Fore.ForecastId ) WHERE i.IntervalStart between @p0 AND @p1
- When searching on DateTime columns for a date range, use “between” operator instead of >, >=, <, <=
- Type Conversions when comparing columns and variables. Previously the ID list was a varchar variable. ForecastId column is a bigint and this was being compared to the IDList varchar column. Sometimes due to type conversions, SQL isn’t able to estimate the right index to use. By storing the required ForecastIds in a temptable variable, with bigint field, and comparing this to the ForecastID column in the table, the Query optimizer is able to estimate and use the right index. Hence the drop in Cost by 99%
- If it’s possible to get rid of the dynamic SQL, and instead convert this query into a Stored Procedure, that would be ideal. Will need to check the application code to see where this is called from and if this is feasible.
- implements
-
CFAMX-9757 Improve performance of Forecasting Query
-
- Closed
-
1.
|
Review Test Scripts |
![]() |
Ready for QA | Unassigned |
|
||||||
2.
|
Deploy |
![]() |
Ready for QA | Unassigned |
|
||||||
3.
|
Functional Review with QA |
![]() |
Ready for Developer | Unassigned |
|
||||||
4.
|
DIT |
![]() |
Ready for Developer | Unassigned |
|