March 28, 2007 at 9:24 am
Hi,
We have recently migrated from SQL 2000 to SQL 2005, and as part of this we rebuilt all indexes and updated all statistics after setting the compatibility level to 90.
I would have expected performance to be at least as good as it was on 2000, however a particular SP has gone from taking approximately 6 seconds on 2000 to 70+ seconds on 2005 when called through our application, but the exact same call using SSMS with identical parameters (as proven with SQL Profiler) takes around 200ms to complete.
Also, when comparing the two calls in Profiler, the following differences in statistics are displayed:
Application Call
CPU 68578
Reads 15712877
Duration 70019
SSMS Call
CPU 125
Reads 650
Duration 223
I'd be grateful if anyone's got any ideas as to why this SP should perform so badly when called through our application?
March 28, 2007 at 11:04 am
Is your application passing the parameters in a way that might fool SQL server into thinking that a decimal is an integer (for example). To clarify, suppose that one of the columns that's queried has been defined as decimal. If your proc parameter for this particular column is defined as a different type (int for example), SQL server might not use an available index.
Carlos
March 29, 2007 at 12:08 am
Have a look at the exec plan for both, see what the differences are.
In management studio, just run with the execution plan option on. for the application run one, you can get the plan from the proc cache with the sys.dm_exec_query_plan dmv.
If you know the spid that the app's using then you can run the following
SELECT
* FROM sys.dm_exec_requests er CROSS APPLY sys.dm_exec_query_plan(er.plan_handle)
WHERE er.session_id = @AppSpid
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
March 29, 2007 at 1:14 am
also search your xml-execution plan for "CONVERT_IMPLICIT" . This can ruin your execution !
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution ๐
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
March 29, 2007 at 3:06 am
Ok, I've tried dm_exec_requests, but unfortunately can't run it quick enough to capture the fast-performing instance of the query. However, this should come in useful in future!!
I've re-run profiler and captured the xml-execution plan for both queries and there are differences, the main ones being the indexes used on the two largest tables in the query. Is there a logical reason why the optimizer would use different plans when called from within Crystal Report and from within SSMS?
To rule out Crystal as the cause, we have developed a test procedure that simply fires an SQL command, and we have set this to fire the exact code as copied from Profiler. The results of this are that the SP still takes 70 seconds when called from a .NET environment and a fifth of a second when called from SSMS.
I've checked for CONVERT_IMPLICIT in the xml-execution plan and can't see this anywhere. Am I right in thinking that you would see this if the data-type of the parameter differs from the data-type of the field you're comparing it to? If so, I learned a harsh lesson some time back with this one so it's one of the first things I check for!
Thanks for your help so far guys
March 29, 2007 at 3:14 am
It could be parameter sniffing. I wouldn't think so if you used the proc in SSMS, but is a possibility.
Can you post the proc, the code you used to call it from .net and the sections of the exec plan that differ? It would really help in getting to the bottom of the problem.
Do the connection settings differ from the .net test and management studio? (ansi nulls, etc)
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
March 29, 2007 at 4:10 am
Thanks Gail, following should be everything you've asked for but please let me know if I've missed anything.
-------------------------------------------
Stored Procedure
CREATE PROCEDURE [dbo].[usp_Report_010810_GrossProfit_ProductDetailed_ByDeliveryDepot]
@chvStartDepot VARCHAR(4),
@chvEndDepot VARCHAR(4),
@dtsStartDate DATETIME,
@dtsEndDate DATETIME,
@chvStartProduct VARCHAR(7),
@chvEndProduct VARCHAR(7)
AS
SET NOCOUNT ON
SELECT Invoice.LoadingDepot,
InvoiceItem.Product,
Product.Description,
Invoice.InvoiceNo,
Invoice.ThirdPartyOrder,
Invoice.AccountNo,
ISNULL(Account.Name,'') AS Name,
CONVERT(NVARCHAR,Invoice.InvoiceDate,103) AS InvoiceDate,
SUM(InvoiceItem.[Value]) AS TotalInvoiceValue,
SUM(ROUND(InvoiceItem.CostPrice * InvoiceItem.Quantity,2)) AS TotalInvoiceCost,
SUM(InvoiceItem.Quantity) AS Volume,
(SUM(InvoiceItem.[Value]) - SUM(ROUND(InvoiceItem.CostPrice * InvoiceItem.Quantity,2))) AS GrossProfit,
CONVERT(DECIMAL(10,2), CASE WHEN SUM(InvoiceItem.[Value]) = 0 THEN 0 ELSE ROUND(((SUM(InvoiceItem.[Value]) - SUM(ROUND(InvoiceItem.CostPrice * InvoiceItem.Quantity,2))) / SUM(InvoiceItem.[Value])) * 100,2) END) AS GrossProfitPercent,
CONVERT(DECIMAL(10,4), CASE WHEN SUM(InvoiceItem.Quantity) = 0 THEN 0 ELSE ROUND((SUM(InvoiceItem.[Value]) - SUM(ROUND(InvoiceItem.CostPrice * InvoiceItem.Quantity,2))) / SUM(InvoiceItem.Quantity),4) END) AS MarginPerLitre
FROM Invoice WITH (NOLOCK)
INNER JOIN InvoiceItem WITH (NOLOCK) ON Invoice.InvoiceNo = InvoiceItem.InvoiceNo AND Invoice.AccountType = InvoiceItem.AccountType
INNER JOIN Product WITH (NOLOCK) ON InvoiceItem.Product = Product.Product
LEFT OUTER JOIN Account WITH (NOLOCK) ON Invoice.AccountNo = Account.AccountNo AND Invoice.AccountType = Account.AccountType
WHERE Invoice.LoadingDepot BETWEEN @chvStartDepot AND @chvEndDepot
AND Invoice.InvoiceDate BETWEEN @dtsStartDate AND @dtsEndDate
AND InvoiceItem.Product BETWEEN @chvStartProduct AND @chvEndProduct
GROUP BY Invoice.LoadingDepot,
InvoiceItem.Product,
Product.Description,
Invoice.InvoiceNo,
Invoice.ThirdPartyOrder,
Invoice.AccountNo,
Account.Name,
Invoice.InvoiceDate
ORDER BY Invoice.LoadingDepot,
InvoiceItem.Product,
Invoice.InvoiceNo
GO
------------------------------------------
VB.NET Code
Public Function getDataSetStr(ByVal strQuery As String) As Data.DataSet
Dim oDataAdapter = New SqlDataAdapter(strQuery, oConnection)
Dim ds = New DataSet
Try
'Fill the DataSet with the results of the query.
oDataAdapter.Fill(ds)
Catch oException As Exception
Throw oException
Finally
oConnection.Close()
oDataAdapter = Nothing
oConnection = Nothing
End Try
getDataSetStr = ds
End Function
-----------------------------------------------
Appliaction Connection string
โData Source=TEDEM2\TEDEM2; Network Library=dbnmpntw; Initial Catalog=eM2; Integrated Security=SSPIโ
----------------------------------------
Database Properties
--------------------------------------------------
SSMS Connection Properties
---------------------------------------------------
Execution plans (NB Both execution plans are included in the same image with the 1st execution plan being the one that performs slowly. As the image was so large I excluded the 3 steps to the left that were identical)
March 29, 2007 at 4:24 am
Can't see the images, because they're on your machine. Could you paste the differeing lines of the exec plan, as well as the query that you called the .net with?
Thanks
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
March 29, 2007 at 4:47 am
Sorry Gail, I know I'm being dense but how do I paste the differing lines, as they're shown as a graphic in Profiler and I can't find a way to extract them as text?!
The query is consistently executed as follows:
exec "database"."dbo"."usp_Report_010810_GrossProfit_ProductDetailed_ByDeliveryDepot";1 'ETFJ', 'ETFJ', '', 'ZZZZZZZ', {ts '2007-03-27 00:00:00'}, {ts '2007-03-28 00:00:00'}, '',
'ZZZZZZZ'
, '', 'ZZZZZZZ', '', 'D'
March 29, 2007 at 4:57 am
Gail, not sure why this works but I've declared a second set of variables after the parameters and passed the parameters into these. I've then changed the WHERE clause to use my new set of variables and the SP completes consistently in 7 seconds regardless of where it is called from.
Although I'm delighted I can get the report to finish significantly faster, it seems bizarre that when i call it from within SSMS it should now be significantly slower than it was previously when called from SSMS?!
March 29, 2007 at 5:04 am
Then it's definatly a parameter sniffing issue. Can you try another .net experiment please?
First revert back to the old stored proc, without the variables.
This time, instead of passing the entire exec string to the data set, try going through the command object, type stored procedure and explicitly add the parameters (I think its .parameters.add from the command object, but I'm not a .net programmer)
If this executes quick, see if there's a way in crystal to specify parameters for the stored proc.
My guess is that the query is coming through, not as an rpc call with params, but as an ahdoc sql statement and the optimiser's misreading one of the param types or values.
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
March 29, 2007 at 5:07 am
Random thought - is you server 64 bit or 32? How much memory?
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
March 29, 2007 at 5:27 am
Start sqlprofiler for a while to captura all the load.
It shows the connection and execution settings !
You can load it into a table and analyse later on.
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution ๐
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
March 29, 2007 at 6:02 am
Gail, unfortunately I'm not a .Net developer either, but I've spoken to our team and although the test procedure we developed DID pass an ad-hoc query to the database, all procedures use the parameter.add method, and the Crystal Reports that do not use a data-set (which includes the one we have an issue with) also specify the parameters to be passed. As far as we are aware, if you're not passing a data-set to Crystal Reports, the only other method is to option is to specify the parameters. Hope I've explained that correctly!
We're using 32-bit Enterprise Edition on a 4 processor code machine with 8GB of RAM, of which 6GB is dedicated to SQL Server.
I've read more about parameter sniffing and am now convinced that this is the root of our problems so will make changes to the affected procedures to hopefully give the optimizer a heads-up on typical values that will be passed in, or at least use local variables when there is no typical value.
Thanks for your help guys.
March 29, 2007 at 6:18 am
If you're likely to get widely varying parameters, try marking the proc WITH RECOMPILE.
Alternativly, if some params are more common than others, you can try the OPTION (OPTIMISE FOR... hint on the query. Both are pretty much last-resort fixes and don't always work, but is worth a try.
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
Viewing 15 posts - 1 through 15 (of 18 total)
You must be logged in to reply to this topic. Login to reply