November 13, 2013 at 9:57 am
This script below runs 5 sec with hard-coded value
and 45 sec using parameter!
guys, any ideas why?
Hard-coded version:
select gl_acct_detail.*
from
gl_acct_detail
inner join
gl_acct_code
on gl_acct_detail.acct_code = gl_acct_code.acct_code
and gl_acct_detail.co_code = gl_acct_code.co_code
where
gl_acct_code.co_code = '5450'
Parameter version:
declare
@P_CO_CODE varchar(10)
set @P_CO_CODE = '5450'
select gl_acct_detail.*
from
gl_acct_detail
inner join
gl_acct_code
on gl_acct_detail.acct_code = gl_acct_code.acct_code
and gl_acct_detail.co_code = gl_acct_code.co_code
where
gl_acct_code.co_code = @P_CO_CODE
Please see attached Execution plans.
November 14, 2013 at 2:26 pm
The optimizer doesn't know the value of the variable in the second query it is changing the query plan to use a clustered index scan rather than a seek as shown by your plans.
Here is an article with a good explanation and suggestions:
http://blogs.technet.com/b/mdegre/archive/2012/03/19/what-is-parameter-sniffing.aspx
November 14, 2013 at 2:56 pm
First, here's the problem (looking at the 45 sec plan):
This looks like parameter sniffing; your cardinality estimates are messed. Your query is scanning an index that has 5M+ rows to only returning 18K+ rows. The 5sec version is splitting the work up evenly between a non-clustered index seek and a non-clustered index scan.
I am going to make some suggestions but hope someone else chimes in too; I am no query plan guru and hope to learn something from this thread too. That said, here's some things I would do to tackle this:
The quick and dirty fix might be to create the missing non-clustered index that your 45 second plan is complaining about:
USE [proddb6]
GO
CREATE NONCLUSTERED INDEX lets_let_you_name_this_index
ON [dbo].[gl_acct_detail] ([co_code])
INCLUDE ([src_co_code],[accounting_period],[orig_acct_period],[fiscal_year],[fiscal_per_nbr],[batch_nbr],[seq_nbr],[line_nbr],[org_code],[acct_code],[currency_code],[sys_doc_type_code],[usr_doc_type_suf],[doc_nbr],[doc_date],[usr_batch_id],[input_date],[per_end_date],[update_time],[evc_type_code],[evc_code],[fcs_desc_skey],[ref_nbr],[ref_date],[db_amt_ac],[db_amt_cc],[cr_amt_ac],[cr_amt_cc],[prj_code],[phase_code],[task_code],[inv_grouping_code],[intern_ref_nbr],[mod_date])
GO
A new plan should be complied when you run that query again and It should use the new non-clustered index with a more accurate cardinality estimate and much faster results. I say "quick and dirty" because, in this case you are basically re-creating the clustered index and not solving the underlying cause of the parameter sniffing.
Other options (and I can't say which is best for your situation) could include:
1) You could optimize the slow query on every execution by running the using OPTION (RECOMPLIE)
2) You could optimize for a typical parameter; e.g. you could include OPTION (OPTIMIZE FOR (@P_CO_CODE=5450))
3) You could optimize for UNKNOWN by including (OPTIMIZE FOR UNKNOWN)
Query hints should be a last resort and have their risks but, for this situation, they may be something to explore. Again, I hope someone else chimes in. Let me know if this helps.
-- Itzik Ben-Gan 2001
November 14, 2013 at 3:05 pm
ahperez (11/14/2013)
Here is an article with a good explanation and suggestions:
http://blogs.technet.com/b/mdegre/archive/2012/03/19/what-is-parameter-sniffing.aspx%5B/quote%5D
+1! When I posted my comment I did not see any other comments. Erland Sommarskog has a great article which covers this topic in detail:
Slow in the Application, Fast in SSMS? Understanding Performance Mysteries
-- Itzik Ben-Gan 2001
November 15, 2013 at 9:25 am
Thanks guys for your response !
ahperez is referring to a good article about parameter sniffing
but it mostly makes recommendations for the case of stored procedure.
In my case it is a simple script with a parameter.
I am willing to try number # 3 from this article:
1. Using dummy variables that are not directly displayed on parameters also ensure execution plan stability without need to add recompile hint, example below:
create procedure dbo.SearchProducts
@Keyword varchar(100)
As
Declare @Keyworddummy as varchar(100)
Set @Keyworddummy = @Keyword
select * from Products where Keyword like @Keyworddummy
2. To prevent this and other similar situations, you can use the following query option:
OPTIMIZE FOR
RECOMPILE
3. Disable auto-update statistics during the batch
But not sure how to do it.
Anybody knows?
November 15, 2013 at 9:34 am
Alan B.
Unfortunately it is Third Party Vendor Application database.
We are not allowed to change schema.
We store all our code in external database where we pull data from views that
are just a mirror of tables in proddb6.
( create view gl_acct_detail as select * from proddb6.dbo.gl_acct_detail )
November 18, 2013 at 7:40 am
Well, since this is a third-party app this might not be a practical option. Auto update stats is a database level command, impacting the whole db. Below is the command to disable auto update and you will also want to add the command to re-enable them when the query finishes.
ALTER DATABASE YourDBName
SET AUTO_UPDATE_STATISTICS OFF
Please test your results and let me know if this speeds things up without impacting other operations.
November 18, 2013 at 7:56 am
ahperez (11/18/2013)
Well, since this is a third-party app this might not be a practical option. Auto update stats is a database level command, impacting the whole db. Below is the command to disable auto update and you will also want to add the command to re-enable them when the query finishes.ALTER DATABASE YourDBName
SET AUTO_UPDATE_STATISTICS OFF
Please test your results and let me know if this speeds things up without impacting other operations.
I would NEVER, EVER recommend turning off auto update stats. that is something you might do only in a high transaction environment where you have put something in place to explicitly handle statistics being updated.
much better to leave that on, and add a job that updates statistics on onyl specific tables that are heavily inserted/updated/deleted.
Auto update stats maybe be a database level command, but it actually occurs on an individual table basis, when the # of rows changed in a table reaches 20% + 500 rows.
so a table with a billion rows needs to see a HUGE number of rows changed before auto updates executes for that table, and it takes a tiny fraction of that number of rows for out of date statistics to change to severely impact performance.
so if you are seeing a performance hit on table gl_acct_detail due to statistics, for example run this command:
UPDATE STATISTICS dbo.gl_acct_detail WITH FULLSCAN ;
schedule that to run more often, say once or twice a day, depending on the frequency of the tables that get changed.
I've heard of some environments updating stats on specific tables once an hour on hight transaction tables, for example.
Lowell
November 18, 2013 at 8:11 am
Thanks Lowell, yours is a better answer for most environments. Mine is fairly unique with very large weekly/monthly loads. To speed the load we update stats later in a stored proc.
Viewing 9 posts - 1 through 8 (of 8 total)
You must be logged in to reply to this topic. Login to reply