- Keep the base procedure, but have it call one of two other procedures depending on @AccountType value. Each of these sub procedures will be compiled on appropriate values.
- Use WITH RECOMPILE on the procedure, so that it recompiles with every execution.
- Declare a new parameter in the procedure @AccountType2, which is set to @AccountType within the procedure and @AccountType2 is the one referenced in the query. This way, when the optimiser "sniffs" the @AccountType2 parameter it will always be the same initial value. Note, the sniffing happens when the proc is called, not when the statement is about to be executed.
- Part of the solution might be to disable automatic updating of statistics and to update statistics manually (followed by performance and plan flipping tests). Note, there is a lot more to statistics and this is probably only appropriate for a tight OLTP environment. Disabling automatic updating can negatively impact performance of ad hoc queries that are using recently inserted key ranges. However, note; updated statistics will trigger recompilation of dependent query plans.
- Use the OPTIMISE FOR hint. However this often is not appropriate as there is no appropriate value that will continue to be appropriate in the future. Note, since 2008, you can OPTIMIZE FOR UNKNOWN, which is similar to point 3 above.
- Use the KEEPFIXED PLAN hint, which inhibits recompiles due to optimality issues.
- Use a plan guide. Technically, this solution works, but it does add to operational overhead and the plan guide may not be appropriate in the future.
- Enable Trace Flag 4136, which will disable parameter sniffing. Since parameter sniffing is generally a performance benefit, this is a bit drastic.
- Force the use of a particular join and/or index. This is frowned upon for good reason.
Note, SET options can affect plan creation and reuse. This means that if you want to use a plan in Management Studio the same as the application, you will need to ensure your SET options are the same as the application. SET options that affect reuse include ANSI_NULL_DFLT_ON, ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, DATEFIRST, DATEFORMAT, FORCEPLAN, LANGUAGE, NO_BROWSETABLE, NUMERIC_ROUNDABORT, and QUOTED_IDENTIFIER. The SET options used in the compile can be found in the query_plan under the tag StatementSetOptions.
Again, plan flipping is not something that should cause you anxiety. However, it is good practice for database developers to recognise plan flipping possibilities and use the techniques above to minimise its causing performance issues. Most plans are very good and better than the above average developer. I encourage developers to make use of point 1 above, where there are different "classes" of parameter values.
Here is a useful query to see the query plans for a particular procedure. Click on the query_plan to see a graphical plan view. Note, this query is looking for procedures only. You can also view p.objtype='Adhoc' for ad hoc queries that are compiled as "temporary procs". You are able to view the parameter values used for compilation of the query plan from the query_plan of the first query above. Simply copy out the query_plan xml and search for ParameterList.
SELECT p.*,st.text, qp.query_plan
FROM sys.dm_exec_cached_plans
p
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
cross apply sys.dm_exec_query_plan(p.plan_handle) as qp
WHERE st.text LIKE
'%MyProcedureName%'
and p.objtype='Proc'
SELECT * FROM sys.dm_exec_query_stats
WHERE plan_handle in
(SELECT
plan_handle FROM sys.dm_exec_cached_plans
p
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
WHERE text LIKE '%MyProcedureName%'
and p.objtype='Proc')
Here is a query to list all the procedures that have more than one query plan.
SELECT db_name(st.dbid) DbName, st.text, count(distinct plan_handle)
FROM sys.dm_exec_cached_plans
p
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
cross apply sys.dm_exec_query_plan(p.plan_handle) as qp
WHERE p.objtype='Proc'
group
by db_name(st.dbid),st.text having count(distinct plan_handle)
>1
order
by 1,2
No comments:
Post a Comment