Showing posts with label sp_prepare. Show all posts
Showing posts with label sp_prepare. Show all posts

Monday, March 19, 2012

Another Slow Execution Plan with sp_prepare

We are having a similar problem. We have a 3rd party application that uses
sp_prepare and then executes it. The problem is the query plan from the
sp_prepare statement is different than if you run the statement using
sp_execute or sp_executesql. I run the following statements:
declare @.P1 int
exec sp_prepare @.P1 output, N'@.P1 bigint,@.P2 bigint,@.P3 bigint,@.P4
bigint,@.P5 bigint,@.P6 bigint,@.P7 bigint,@.P8 bigint', 'SELECT SHAPE
,S_.eminx,S_.eminy,S_.emaxx,S_.emaxy ,SHAPE.fid F_fid,SHAPE.numofpts
F_numofpts,SHAPE.entity F_entity,SHAPE.points F_points FROM (SELECT DISTINC
T
sp_fid,eminx,eminy,emaxx,emaxy FROM SDE.SDE.s162 SP_ WHERE SP_.gx >= @.P1 AN
D
SP_.gx <= @.P2 AND SP_.gy >= @.P3 AND SP_.gy <= @.P4 AND SP_.eminx <= @.P5 AND
SP_.eminy <= @.P6 AND SP_.emaxx >= @.P7 AND SP_.emaxy >= @.P8 ) S_
,SDE.SDE.ENTORDERLINESEGMENT, SDE.SDE.f162 SHAPE WHERE S_.sp_fid = SHAPE.fi
d
AND SDE.SDE.ENTORDERLINESEGMENT.SHAPE = S_.sp_fid AND (( ORDERID in (16320,
16825) ))', 1 select @.P1
exec sp_execute 1, 166, 169, 219, 224, 90269119, 119480870, 88777840,
117193071
This takes almost a minute to return.
Then I run this statement:
declare @.P1 int
exec sp_prepare @.P1 output, N'@.P1 bigint,@.P2 bigint,@.P3 bigint,@.P4
bigint,@.P5 bigint,@.P6 bigint,@.P7 bigint,@.P8 bigint', 'SELECT SHAPE
,S_.eminx,S_.eminy,S_.emaxx,S_.emaxy ,SHAPE.fid F_fid,SHAPE.numofpts
F_numofpts,SHAPE.entity F_entity,SHAPE.points F_points FROM (SELECT DISTINC
T
sp_fid,eminx,eminy,emaxx,emaxy FROM SDE.SDE.s162 SP_ WHERE SP_.gx >= @.P1 AN
D
SP_.gx <= @.P2 AND SP_.gy >= @.P3 AND SP_.gy <= @.P4 AND SP_.eminx <= @.P5 AND
SP_.eminy <= @.P6 AND SP_.emaxx >= @.P7 AND SP_.emaxy >= @.P8 ) S_
,SDE.SDE.ENTORDERLINESEGMENT, SDE.SDE.f162 SHAPE WHERE S_.sp_fid = SHAPE.fi
d
AND SDE.SDE.ENTORDERLINESEGMENT.SHAPE = S_.sp_fid AND (( ORDERID in (16320,
16825) ))', 1 select @.P1
DBCC Freeproccache
exec sp_execute 1, 166, 169, 219, 224, 90269119, 119480870, 88777840,
117193071
This returns in under 1 second. The only difference is the freeproccache.
The first sp_execute is using the store query plan from cache. The second i
s
forced to compile a new execution plan which is much faster.
Does anyone know why the sp_prepare query execution plan would be slower?
Shouldn't it create the same plan?
Also, how did you change sp_prepare to use varchar instead of nvarchar? I
get an error stating the sp_prepare expects @.statement of type
ntext/nchar/nvarchar.
Thanks for any help.
Doug Matney
"lorinda" wrote:

> This makes sense. Looks like I will have to find a work around. Thanks
> again for your help!
>dmatney (dmatney@.discussions.microsoft.com) writes:
> We are having a similar problem. We have a 3rd party application that
> uses sp_prepare and then executes it. The problem is the query plan
> from the sp_prepare statement is different than if you run the statement
> using sp_execute or sp_executesql. I run the following statements:
>...
> This returns in under 1 second. The only difference is the
> freeproccache. The first sp_execute is using the store query plan from
> cache. The second is forced to compile a new execution plan which is
> much faster.
> Does anyone know why the sp_prepare query execution plan would be slower?
> Shouldn't it create the same plan?
Since I have never understood the point with sp_prepare, or what it really
achieves, this had me stumped at first, but I think I know the answer.
SQL Server is fond of parameter sniffing. This means that when you
run something that has parameters, be that a stored procedure or
sp_executesql, it looks at the parameter values and take these as
guidance for the plan.
But when you run sp_prepare there are no actual parameter values. Still
SQL Server builds a plan this point, using standard assumptions. Your
query includs a lot of >=. I believe the standard assumption here is
a 30% hit-rate. This is far above the limit where a non-clustered index
is deemed to be more expensive than a table scan.
When you flush the cache, there is no plan for sp_execute to use, so
a new plan has to be created, and since the parameter values are now
available, the optimizer can "sniff" them, and build a plan that better
fits the actual values. Presumably, the input values are close to the
edges.
Since this is from a third-party app, I guess your options to address
this are limited. You could investigate if you could make any of the
involved indexes clustered, but assuming that there is a clustered index
already, this could have ramifications elsewhere in the application.
If you are on SQL 2005, you could add a plan guide, which is quite an
advanced exercise.

> Also, how did you change sp_prepare to use varchar instead of nvarchar? I
> get an error stating the sp_prepare expects @.statement of type
> ntext/nchar/nvarchar.
I guess you don't. It's the same with sp_executesql. The parameter is
ntext (nvarchar(MAX) in SQL 2005), and since this is a built-in stored
procedure, there is no implicit conversion from varchar.
Erland Sommarskog, SQL Server MVP, esquel@.sommarskog.se
Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/pr...oads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodin...ions/books.mspx|||Thanks. I think you are correct about the parameter sniffing. I have tried
creating a plan guide using a Recompile hint and it works for this particula
r
sp_prepare but since the application doesn't use parameters for all values (
the OrderID in clause uses real values) I can't get it to work every time.
I'm going to do a little more research and see if we can't convince the
application developer to use a different method. Thanks again for your help
.
"Erland Sommarskog" wrote:

> dmatney (dmatney@.discussions.microsoft.com) writes:
> Since I have never understood the point with sp_prepare, or what it really
> achieves, this had me stumped at first, but I think I know the answer.
> SQL Server is fond of parameter sniffing. This means that when you
> run something that has parameters, be that a stored procedure or
> sp_executesql, it looks at the parameter values and take these as
> guidance for the plan.
> But when you run sp_prepare there are no actual parameter values. Still
> SQL Server builds a plan this point, using standard assumptions. Your
> query includs a lot of >=. I believe the standard assumption here is
> a 30% hit-rate. This is far above the limit where a non-clustered index
> is deemed to be more expensive than a table scan.
> When you flush the cache, there is no plan for sp_execute to use, so
> a new plan has to be created, and since the parameter values are now
> available, the optimizer can "sniff" them, and build a plan that better
> fits the actual values. Presumably, the input values are close to the
> edges.
> Since this is from a third-party app, I guess your options to address
> this are limited. You could investigate if you could make any of the
> involved indexes clustered, but assuming that there is a clustered index
> already, this could have ramifications elsewhere in the application.
> If you are on SQL 2005, you could add a plan guide, which is quite an
> advanced exercise.
>
> I guess you don't. It's the same with sp_executesql. The parameter is
> ntext (nvarchar(MAX) in SQL 2005), and since this is a built-in stored
> procedure, there is no implicit conversion from varchar.
> --
> Erland Sommarskog, SQL Server MVP, esquel@.sommarskog.se
> Books Online for SQL Server 2005 at
> http://www.microsoft.com/technet/pr...oads/books.mspx
> Books Online for SQL Server 2000 at
> http://www.microsoft.com/sql/prodin...ions/books.mspx
>

Another Slow Execution Plan with sp_prepare

Thanks to Erland, my issue with sp_prepare of NVarchar and Varchar has been
resolved by changing a parameter in JDBC to force it to use Varchar instead
of NVarchar. JDBC's default setting uses NVarchar. Look under Subject "Slo
w
Query with sp_prepare and sp_execute" to see the thread.
However, I now have a problem with the INT datatype in my prepare
statements. I have tried using various numeric data types with sp_prepare.
But none gives me a response time as fast as running a single query.
For example, I have the following table:
CREATE TABLE Segment (
SegmentId int NOT NULL,
SegmentSetId int NOT NULL,
Sequence varchar (255) NOT NULL,
CONSTRAINT PK_Segment PRIMARY KEY CLUSTERED (SegmentId)
With the below index:
CREATE INDEX IX_Segment ON Segment(SegmentSetId)
The table has 6 million rows. I am using prepare statments in a JDBC
environment. When the below prepare statement is used, the execution plan
uses index scan which generates a slow response time:
Declare @.P1 int
Exec sp_prepare @.P1 output,
N'@.P1 int',
N'select * from Segment where SegmentSetId=@.P1'
Select @.P1
Exec sp_execute @.P1,649
Execution Tree
--
Parallelism(Gather Streams)
|--Clustered Index Scan(OBJECT:([Dev].[dbo].[Segment].[PK_Segment]),
WHERE:([Segment].[SegmentSetId]=[@.P1]))
Now, if I just run the query with all the parameters already provided, the
execution plan
uses index s which generates a fast response time:
select * from Segment where SegmentSetId=649
Execution Tree
--
Bookmark Lookup(BOOKMARK:([Bmk1000]), OBJECT:([Dev].[dbo].[Segment]))
|--Index S(OBJECT:([Dev].[dbo].[Segment].[IX_Segment]),
SEEK:([Segment].[SegmentSetId]=Convert([@.1])) ORDERED FORWARD)
Can anyone give me a hint how to resolve this one? What can I use in my
prepared statement to force the optimizer to use an index s instead of an
index scan?
Again, any help will be greatly appreciated!Hi
If you add to this statement (AND 1=1) do you see an index s?
N'select * from Segment where SegmentSetId=@.P1 and 1=1'
"lorinda" <lorinda@.community.nospam> wrote in message
news:121EF92B-554B-4D96-AFCA-C6E6F0A5D757@.microsoft.com...
> Thanks to Erland, my issue with sp_prepare of NVarchar and Varchar has
> been
> resolved by changing a parameter in JDBC to force it to use Varchar
> instead
> of NVarchar. JDBC's default setting uses NVarchar. Look under Subject
> "Slow
> Query with sp_prepare and sp_execute" to see the thread.
> However, I now have a problem with the INT datatype in my prepare
> statements. I have tried using various numeric data types with
> sp_prepare.
> But none gives me a response time as fast as running a single query.
> For example, I have the following table:
> CREATE TABLE Segment (
> SegmentId int NOT NULL,
> SegmentSetId int NOT NULL,
> Sequence varchar (255) NOT NULL,
> CONSTRAINT PK_Segment PRIMARY KEY CLUSTERED (SegmentId)
> With the below index:
> CREATE INDEX IX_Segment ON Segment(SegmentSetId)
> The table has 6 million rows. I am using prepare statments in a JDBC
> environment. When the below prepare statement is used, the execution plan
> uses index scan which generates a slow response time:
> Declare @.P1 int
> Exec sp_prepare @.P1 output,
> N'@.P1 int',
> N'select * from Segment where SegmentSetId=@.P1'
> Select @.P1
> Exec sp_execute @.P1,649
> Execution Tree
> --
> Parallelism(Gather Streams)
> |--Clustered Index Scan(OBJECT:([Dev].[dbo].[Segment].[PK_Segment]),
> WHERE:([Segment].[SegmentSetId]=[@.P1]))
> Now, if I just run the query with all the parameters already provided, the
> execution plan
> uses index s which generates a fast response time:
> select * from Segment where SegmentSetId=649
> Execution Tree
> --
> Bookmark Lookup(BOOKMARK:([Bmk1000]), OBJECT:([Dev].[dbo].[Segment]))
> |--Index S(OBJECT:([Dev].[dbo].[Segment].[IX_Segment]),
> SEEK:([Segment].[SegmentSetId]=Convert([@.1])) ORDERED FORWARD)
> Can anyone give me a hint how to resolve this one? What can I use in my
> prepared statement to force the optimizer to use an index s instead of
> an
> index scan?
> Again, any help will be greatly appreciated!
>|||lorinda (lorinda@.community.nospam) writes:
> However, I now have a problem with the INT datatype in my prepare
> statements. I have tried using various numeric data types with
> sp_prepare. But none gives me a response time as fast as running a
> single query.
> For example, I have the following table:
> CREATE TABLE Segment (
> SegmentId int NOT NULL,
> SegmentSetId int NOT NULL,
> Sequence varchar (255) NOT NULL,
> CONSTRAINT PK_Segment PRIMARY KEY CLUSTERED (SegmentId)
> With the below index:
> CREATE INDEX IX_Segment ON Segment(SegmentSetId)
> The table has 6 million rows. I am using prepare statments in a JDBC
> environment. When the below prepare statement is used, the execution plan
> uses index scan which generates a slow response time:
>...
> Now, if I just run the query with all the parameters already provided, the
> execution plan
> uses index s which generates a fast response time:
> select * from Segment where SegmentSetId=649
So this is a completely different issue from the varchar thing.
This is the classical choice between using a non-clustered index or
scanning the table. If there are two rows with SegmentSetID = 649 the
index is alright. But if there is a million, it's faster to find them
by scanning the table, because using the index would require many
page accesses to the same page.
SQL Server has statistics from which it makes estimates. At least for
stored procedure it sniffs the input parameter value when it builds
the plan and puts in cache. I would assume that this is the case
with sp_prepare as well. So I would expect it to use the index. Then
again, if you first attempt was with SegmentSetID = 0, and there are
a million rows with id 0, then what ended up in the cache was the
clustered index scan.
You can force the index with an index hint:
SELECT * FROM Segment WITH (INDEX = IX_Segment) WHERE ...
but this can come back bite when you are asking for that SegmentSetID
with lots of rows.
Erland Sommarskog, SQL Server MVP, esquel@.sommarskog.se
Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/pr...oads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodin...ions/books.mspx|||This makes sense. Looks like I will have to find a work around. Thanks
again for your help!