473,395 Members | 1,377 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

Stored procedure and query plans different

I have a stored procedure that suddenly started performing horribly.
The query plan didn't look right to me, so I copy/pasted the code and
ran it (it's a single SELECT statement). That ran pretty well and used
a query plan that made sense. Now, I know what you're all thinking...
stored procedures have to optimize for variable parameters, etc.
Here's what I've tried to fix the issue:

1. Recompiled the stored procedure
2. Created a new, but identical stored procedure
3. Created the stored procedure with the RECOMPILE option
4. Created the stored procedure with a hard-coded value instead of
aparameter
5. Changed the stored procedure to use dynamic SQL

In every case, performance did not improve and the query plan remained
the same (I could not easily confirm this with the dynamic SQL
version, but performance was still horrible).

I am currently running UPDATE STATISTICS on all of the involved
tables, but that will take awhile.

Any ideas?

Thanks!
-Tom.
Jul 23 '05 #1
10 3713
Thomas R. Hummel (to********@hotmail.com) writes:
I have a stored procedure that suddenly started performing horribly.
The query plan didn't look right to me, so I copy/pasted the code and
ran it (it's a single SELECT statement). That ran pretty well and used
a query plan that made sense. Now, I know what you're all thinking...
stored procedures have to optimize for variable parameters, etc.
Here's what I've tried to fix the issue:

1. Recompiled the stored procedure
2. Created a new, but identical stored procedure
3. Created the stored procedure with the RECOMPILE option
4. Created the stored procedure with a hard-coded value instead of
aparameter
5. Changed the stored procedure to use dynamic SQL

In every case, performance did not improve and the query plan remained
the same (I could not easily confirm this with the dynamic SQL
version, but performance was still horrible).


I would guess that the query depends on an indexed view or an indexed
computed columns, and you are creating stored procedures from Enterprise
Manager or OSQL that has QUOTED_IDENTIFIER off by default.

This setting is saved with the stored procedure, and this setting must
be on, for SQL Server to use indexes on views and computed columns.

--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 23 '05 #2
Excellent suggestion, but that is not the case. The select statement is
from tables only, none of which use computed columns.

Just to be safe, I recreated the stored procedure with QUOTED_IDENTIFIER
on and it did not have any effect on the query plan. I also tried saving
it with ANSI NULLS set to both ON and OFF and neither had an effect.

Last night I was able to update the statistics on all of the tables.
That also had no effect. At the moment I am out of ideas :(

Thanks,

-----------------
Thomas R. Hummel
Senior Database Administrator

*** Sent via Developersdex http://www.developersdex.com ***
Don't just participate in USENET...get rewarded for it!
Jul 23 '05 #3
Thomas R. Hummel (to**************@hotmail.com) writes:
Excellent suggestion, but that is not the case. The select statement is
from tables only, none of which use computed columns.

Just to be safe, I recreated the stored procedure with QUOTED_IDENTIFIER
on and it did not have any effect on the query plan. I also tried saving
it with ANSI NULLS set to both ON and OFF and neither had an effect.

Last night I was able to update the statistics on all of the tables.
That also had no effect. At the moment I am out of ideas :(


Not seeing the code or plans it's difficult to say. But how does the
bad plan look like? Does it involve parallellism? In that case an
OPTION (MAXDOP 1) may do the trick.

--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 23 '05 #4
Sorry for not including this sooner. The query statement and plan are
below. Hopefully the text will be readable in your newsreader or you can
easily cut and paste it.

There is a clustered index on Lead_Action_History.report_date.
Lead_Master, Report_Group, Report_Month, Import_Batch, and
Project_Dispositions are all joined on primary keys.
Project_Disp_Config_Detail is joined using columns that make up the
first two columns of the primary key, which is also clustered.
Lead_Master has about 4.6M records. Lead_Action_History has about 22.5M
records. For the report_date in question there are just over 65K
records. All of the other tables are very small.

As a reminder... the code only uses the plan below when it is run within
the stored procedure. Running it directly in QA uses a more efficient
query plan, also included below.

I tried setting the MAXDOP option to 1 and there was no substantial
change. The parallelism in the below plan was on a pretty insignificant
part of the query.

Thanks!

QUERY:

SELECT RM.report_month_abrev + ' ' + RG.report_Group_descr [Lead
Type],
LAH.calldisp AS [Call Disposition],
CASE WHEN LEN(IsNull(PD.fdisp_id, '')) = 0 THEN ' ' ELSE
IsNull(PD.fdisp_id, '') END AS [FDisp],
CASE WHEN LEN(IsNull(PDCD.output_code, '')) = 0 THEN ' '
ELSE IsNull(PDCD.output_code, '') END AS [Output Code],
COUNT(*) AS [Count],
CAST(PD.calldisp_descr AS VARCHAR(30)) AS [Calldisp
Description]
FROM Lead_Action_History LAH WITH (NOLOCK)
INNER JOIN Lead_Master LM WITH (nolock) ON LAH.lead_seq = LM.lead_seq
INNER JOIN Mercury.dbo.Report_Group RG WITH (nolock) ON
RG.report_group_seq = LM.report_group_seq
INNER JOIN Mercury.dbo.Report_Month RM WITH (nolock) ON
RG.report_month = RM.report_month_seq
INNER JOIN Import_Batch IB WITH (nolock) ON IB.import_batch_seq =
LM.import_batch_seq AND IB.batch_type = 'P'
INNER JOIN Mercury.dbo.Project_Dispositions PD WITH (nolock) ON
PD.project_seq = 10440
AND
PD.calldisp = LAH.calldisp
INNER JOIN Mercury.dbo.Project_Disp_Config_Detail PDCD WITH (nolock)
ON PDCD.project_seq = 10440

AND PDCD.calldisp = LAH.calldisp
WHERE report_date = @in_report_date
AND LAH.calldisp IS NOT NULL
GROUP BY RM.report_month_abrev + ' ' + RG.report_Group_descr,
LAH.calldisp,
PD.fdisp_id,
PD.calldisp_descr,
PDCD.output_code
PLAN WHEN RUN WITHIN STORED PROCEDURE:

StmtText
------------------------------------------------------------------------
------------------------------------------------------------------------
------------------------------------------------------------------------
------------------------------------------------------------------------
--------------------------------------------------------
|--Compute Scalar(DEFINE:([Expr1009]=If
(len(isnull([PD].[FDISP_ID], ''))=0) then ' ' else
Convert(isnull([PD].[FDISP_ID], '')), [Expr1010]=If
(len(isnull([PDCD].[OUTPUT_CODE], ''))=0) then ' ' else
isnull([PDCD].[OUTPUT_CODE], ''),
[Expr1011]=Convert([PD].[CALLDISP_DESCR])))
|--Compute Scalar(DEFINE:([Expr1008]=Convert([Expr1032])))
|--Stream Aggregate(GROUP BY:([Expr1007],
[LAH].[calldisp], [PD].[FDISP_ID], [PD].[CALLDISP_DESCR],
[PDCD].[OUTPUT_CODE]) DEFINE:([Expr1032]=Count(*)))
|--Sort(ORDER BY:([Expr1007] ASC, [LAH].[calldisp]
ASC, [PD].[FDISP_ID] ASC, [PD].[CALLDISP_DESCR] ASC,
[PDCD].[OUTPUT_CODE] ASC))
|--Compute
Scalar(DEFINE:([Expr1007]=[RM].[report_month_abrev]+'
'+[RG].[report_group_descr]))
|--Parallelism(Gather Streams)
|--Nested Loops(Inner Join, OUTER
REFERENCES:([PDCD].[CALLDISP]))

|--Filter(WHERE:([LAH].[calldisp]=[PDCD].[CALLDISP]))
| |--Bookmark
Lookup(BOOKMARK:([Bmk1000]), OBJECT:([ALWOB].[dbo].[Lead_Action_History]
AS [LAH]))
| |--Nested
Loops(Inner Join, OUTER REFERENCES:([LM].[lead_seq]) WITH PREFETCH)
| |--Nested
Loops(Inner Join)
| |
|--Filter(WHERE:([IB].[batch_type]='P'))
| | |
|--Bookmark Lookup(BOOKMARK:([Bmk1004]),
OBJECT:([ALWOB].[dbo].[Import_Batch] AS [IB]))
| | |
|--Nested Loops(Inner Join, OUTER REFERENCES:([LM].[import_batch_seq]))
| | |
|--Bookmark Lookup(BOOKMARK:([Bmk1001]),
OBJECT:([ALWOB].[dbo].[Lead_Master] AS [LM]))
| | |
| |--Nested Loops(Inner Join, OUTER
REFERENCES:([RG].[report_group_seq]))
| | |
| |--Hash Match(Inner Join,
HASH:([RM].[report_month_seq])=([RG].[report_month]),
RESIDUAL:([RG].[report_month]=[RM].[report_month_seq]))
| | |
| | |--Parallelism(Broadcast)
| | |
| | | |--Table Scan(OBJECT:([Mercury].[dbo].[Report_Month]
AS [RM]))
| | |
| | |--Parallelism(Distribute Streams)
| | |
| | |--Clustered Index
Scan(OBJECT:([Mercury].[dbo].[Report_Group].[PK_REPORT_GROUP] AS [RG]))
| | |
| |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1029],
[Expr1030], [Expr1031]))
| | |
| |--Compute
Scalar(DEFINE:([Expr1029]=Convert([RG].[report_group_seq])-1,
[Expr1030]=Convert([RG].[report_group_seq])+1, [Expr1031]=If
(Convert([RG].[report_group_seq])-1=NULL) then 0 else 6|If
(Convert([RG].[report_group_seq])+1=NULL) then 0 else 10))
| | |
| | |--Constant Scan
| | |
| |--Index
Seek(OBJECT:([ALWOB].[dbo].[Lead_Master].[LeadMaster_IDX04] AS [LM]),
SEEK:([LM].[report_group_seq] > [Expr1029] AND [LM].[report_group_seq] <
[Expr1030]),
WHERE:([RG].[report_group_seq]=Convert([LM].[report_group_seq])) ORDERED
FORWARD)
| | |
|--Index Seek(OBJECT:([ALWOB].[dbo].[Import_Batch].[pk_import_batch] AS
[IB]), SEEK:([IB].[import_batch_seq]=[LM].[import_batch_seq]) ORDERED
FORWARD)
| |
|--Clustered Index
Seek(OBJECT:([Mercury].[dbo].[PROJECT_DISP_CONFIG_DETAIL].[ProjectDispCo
nfigDetail_PK] AS [PDCD]), SEEK:([PDCD].[PROJECT_SEQ]=10440) ORDERED
FORWARD)
| |--Index
Seek(OBJECT:([ALWOB].[dbo].[Lead_Action_History].[LeadActionHistory_IDX0
3] AS [LAH]), SEEK:([LAH].[lead_seq]=[LM].[lead_seq] AND
[LAH].[report_date]=[@in_report_date]) ORDERED FORWARD)
|--Clustered Index
Seek(OBJECT:([Mercury].[dbo].[PROJECT_DISPOSITIONS].[PK_PROJECT_DISPOSIT
IONS] AS [PD]), SEEK:([PD].[CALLDISP]=[PDCD].[CALLDISP] AND
[PD].[PROJECT_SEQ]=10440) ORDERED FORWARD)
-----------------------------------------------------------

PLAN WHEN RUN OUTSIDE STORED PROCEDURE:

StmtText
------------------------------------------------------------------------
------------------------------------------------------------------------
------------------------------------------------------------------------
------------------------------------------------------------------------
-
|--Compute Scalar(DEFINE:([Expr1009]=If (len(isnull([PD].[FDISP_ID],
''))=0) then '&nbsp;' else Convert(isnull([PD].[FDISP_ID], '')),
[Expr1010]=If (len(isnull([PDCD].[OUTPUT_CODE], ''))=0) then '&nbsp;'
else isnull([PDCD].[OUTPUT_CODE], ''),
[Expr1011]=Convert([PD].[CALLDISP_DESCR])))
|--Compute Scalar(DEFINE:([Expr1008]=Convert([Expr1027])))
|--Stream Aggregate(GROUP BY:([Expr1007], [LAH].[calldisp],
[PD].[FDISP_ID], [PD].[CALLDISP_DESCR], [PDCD].[OUTPUT_CODE])
DEFINE:([Expr1027]=Count(*)))
|--Sort(ORDER BY:([Expr1007] ASC, [LAH].[calldisp] ASC,
[PD].[FDISP_ID] ASC, [PD].[CALLDISP_DESCR] ASC, [PDCD].[OUTPUT_CODE]
ASC))
|--Compute
Scalar(DEFINE:([Expr1007]=[RM].[report_month_abrev]+'
'+[RG].[report_group_descr]))
|--Nested Loops(Inner Join, OUTER
REFERENCES:([PDCD].[CALLDISP]))
|--Filter(WHERE:([IB].[batch_type]='P'))
| |--Bookmark
Lookup(BOOKMARK:([Bmk1004]), OBJECT:([ALWOB].[dbo].[Import_Batch] AS
[IB]))
| |--Nested Loops(Inner Join,
OUTER REFERENCES:([LM].[import_batch_seq]))
| |--Hash Match(Inner Join,
HASH:([RM].[report_month_seq])=([RG].[report_month]),
RESIDUAL:([RG].[report_month]=[RM].[report_month_seq]))
| | |--Table
Scan(OBJECT:([Mercury].[dbo].[Report_Month] AS [RM]))
| | |--Nested
Loops(Inner Join, OUTER REFERENCES:([LM].[report_group_seq]))
| | |--Nested
Loops(Inner Join, OUTER REFERENCES:([LAH].[lead_seq]) WITH PREFETCH)
| | | |--Hash
Match(Inner Join, HASH:([PDCD].[CALLDISP])=([LAH].[calldisp]),
RESIDUAL:([LAH].[calldisp]=[PDCD].[CALLDISP]))
| | | |
|--Clustered Index
Seek(OBJECT:([Mercury].[dbo].[PROJECT_DISP_CONFIG_DETAIL].[ProjectDispCo
nfigDetail_PK] AS [PDCD]), SEEK:([PDCD].[PROJECT_SEQ]=10440) ORDERED
FORWARD)
| | | |
|--Clustered Index
Seek(OBJECT:([ALWOB].[dbo].[Lead_Action_History].[LeadActionHistory_IDX0
2] AS [LAH]), SEEK:([LAH].[report_date]=[@in_report_date]) ORDERED
FORWARD)
| | |
|--Clustered Index
Seek(OBJECT:([ALWOB].[dbo].[Lead_Master].[pk_lead_master] AS [LM]),
SEEK:([LM].[lead_seq]=[LAH].[lead_seq]) ORDERED FORWARD)
| | |--Clustered
Index Seek(OBJECT:([Mercury].[dbo].[Report_Group].[PK_REPORT_GROUP] AS
[RG]), SEEK:([RG].[report_group_seq]=Convert([LM].[report_group_seq]))
ORDERED FORWARD)
| |--Index
Seek(OBJECT:([ALWOB].[dbo].[Import_Batch].[pk_import_batch] AS [IB]),
SEEK:([IB].[import_batch_seq]=[LM].[import_batch_seq]) ORDERED FORWARD)
|--Clustered Index
Seek(OBJECT:([Mercury].[dbo].[PROJECT_DISPOSITIONS].[PK_PROJECT_DISPOSIT
IONS] AS [PD]), SEEK:([PD].[CALLDISP]=[PDCD].[CALLDISP] AND
[PD].[PROJECT_SEQ]=10440) ORDERED FORWARD)
-----------------
Thomas R. Hummel
Manager, Data Systems
*** Sent via Developersdex http://www.developersdex.com ***
Don't just participate in USENET...get rewarded for it!
Jul 23 '05 #5
Thomas R. Hummel (to**************@hotmail.com) writes:
Sorry for not including this sooner. The query statement and plan are
below. Hopefully the text will be readable in your newsreader or you can
easily cut and paste it.


Admittedly it would have helped if the showplan text would not have
been wrapped. It would maybe have been better if you had placed it in an
attachment.

It would also have helped if you had included the complete query batch
and the complete stored procedure. After all, we are looking for the
differences between the two.

So I make another shot from the hip: in the query batch you have:

DECLARE @recorddate datetime
SELECT @recorddate = <value>
-- query follows here.

And in the stored procedure you have @recorddate as a parameter. This means
that in the query batch, the optimizer is blind to the value, while for the
procedure it is not. And this time, the optimizer makes a better choice
of plans when it does not know the value.

Looking at the query and the plans, it appers that the optimizer has two
choices: take all matching 65000 rows in Lead_Action_History and from
there look up Lead_Master for each of them. Or try to narrow down the rows
in Lead_Master from the other tables, and then approach Lead_Action_History
from Lead_Master.

The performance tells us that of these two approaches, the first is the
one to go. I don't know if you ran UPDATE STATISTICS WITH FULLSCAN, and
I don't know if it would help.

You can of course do the trick of hiding the parameter by copying it to a
local parameter in the procedure, but since the query appears to balance
between two plans, it's only a matter of time, until you get bad performance
again.

In that case an (INDEX = 1) for Lead_Action_History to force the clustered
index, is probably a better option, although that too could backfire the
day the other plan really is the better one.
--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 23 '05 #6

4. Created the stored procedure with a hard-coded value instead of
aparameter


I have had this problem before when in the WHERE clause a datetime column
was being compared with a datetime variable. Hardcoding the datetime into
the query fixed the query plan and proved this was the problem. But you say
you tried this?
Jul 23 '05 #7
Your assumptions on the stored procedure and the query batch were
correct. The batch was simply, DECLARE, SET, query, while the stored
procedure used a paramater and consisted of SET NOCOUNT ON and the
query, using the parameter.

I did an UPDATE STATISTICS WITH FULLSCAN on Sunday when we had some
downtime and it made no difference. Also, I think that I mentioned this,
but I tried hard-coding the paramater value rather than passing anything
and that did not affect the query plan chosen, so I don't think that it
is a matter of binding the value in the batch. I also tried using a
local variable and setting that to the parameter value, again with no
change in the query plan.

I've also tried setting all of the connection specific options that I
could think of (ANSI_NULLS, QUOTED_IDENTIFIER, etc.) specifically during
the creation of the stored procedure and in the batch so that I could be
sure that they were the same in both cases. That also had no effect.

Lastly... I also tried using an optimized hint of INDEX=1 and while that
did change the query plan slightly, it was still very much different
from the plan used with the batch and it was also very inefficient.

Thanks for all of your help on this one. Still has me baffled though :(

-----------------
Thomas R. Hummel
Senior Database Administrator
*** Sent via Developersdex http://www.developersdex.com ***
Don't just participate in USENET...get rewarded for it!
Jul 23 '05 #8
>> 4. Created the stored procedure with a hard-coded value instead of
aparameter
I have had this problem before when in the WHERE clause a datetime column was being compared with a datetime variable. Hardcoding the datetime into the query fixed the query plan and proved this was the problem. But you say you tried this?

Yes, that was indeed something that I tried.

-----------------
Thomas R. Hummel
Senior Database Administrator
*** Sent via Developersdex http://www.developersdex.com ***
Don't just participate in USENET...get rewarded for it!
Jul 23 '05 #9
Thomas R. Hummel (to**************@hotmail.com) writes:
Lastly... I also tried using an optimized hint of INDEX=1 and while that
did change the query plan slightly, it was still very much different
from the plan used with the batch and it was also very inefficient.


Too bad to hear. But without more information about the case, it's
difficult for me to come with any more suggestions.
--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 23 '05 #10
So did you figure out your problem yet?

"Thomas R. Hummel" <to**************@hotmail.com> wrote in message
news:1102436941.be37b3dadf2b109c96f4bfc4a79c6a4e@t eranews...
4. Created the stored procedure with a hard-coded value instead of
aparameter

I have had this problem before when in the WHERE clause a datetime

column
was being compared with a datetime variable. Hardcoding the datetime

into
the query fixed the query plan and proved this was the problem. But

you say
you tried this?

Yes, that was indeed something that I tried.

-----------------
Thomas R. Hummel
Senior Database Administrator
*** Sent via Developersdex http://www.developersdex.com ***
Don't just participate in USENET...get rewarded for it!

Jul 23 '05 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

10
by: Dragonhunter | last post by:
Hello, The aspfaq.com seems to really push stored procedures, and I hear the same advice here all the time. So I want to take the advice. Is it possible to create and practically maintain,...
12
by: serge | last post by:
I have an SP that is big, huge, 700-800 lines. I am not an expert but I need to figure out every possible way that I can improve the performance speed of this SP. In the next couple of weeks I...
7
by: Andrew Mayo | last post by:
Here's a really weird one for any SQL Server gurus out there... We have observed (SQL Server 2000) scenarios where a stored procedure which (a) begins a transaction (b) inserts some rows into...
5
by: mas | last post by:
I have a Stored Procedure (SP) that creates the data required for a report that I show on a web page. The SP does all the work and just returns back a results set that I dump in an ASP.NET...
45
by: John | last post by:
Hi When developing vb.bet winform apps bound to sql server datasource, is it preferable to use SELECTs or stored procedure to read and write data from/to SQL Server? Why? Thanks Regards
2
by: Rajat Katyal | last post by:
Hi: I prepare the statement for execution as follows: PREPARE query(text) as SELECT count(*) FROM transform_customer_billing where inv_no = $1; The problem is Iam not able to execute this...
28
by: mooreit | last post by:
The purpose for my questions is accessing these technologies from applications. I develop both applications and databases. Working with Microsoft C#.NET and Microsoft SQL Server 2000 Production and...
12
by: Jason Huang | last post by:
Hi, In my C# Windows Form application project, I have done all queries on my codes. Now I wanna try using the Stored Procedure. But I am not clear about why using the stored procedure. Would...
20
by: billmaclean1 | last post by:
I need to write a stored procedure that selects from a table and returns the result set. I don't always know the TableSchema that I need to use when qualifying the table at run-time Example:...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.