I have read threads here and here and I get that elapsed time is the total duration of the task – and if the elapsed time is less than CPU time, the query went parallel.
After typing that, I was trying to improve a stored procedure's performance in an area in which we are experiencing some slowness.
The existing TSQL:
Paste the Plan
As you can see, we have 6 parameters that are all optional – a customer lookup where you can use a variety or just one variable to search.
When executed, this procedure goes parallel and the statistics IO and time are as follows:
My initial thought was to rewrite the WHERE
clause to replace the COALESCE
& ISNULL
functions to simply ( @paramCustomerID IS NULL or c.id = @paramCustomerID )
And because I find CTE's more readable than a sub-query, I did change that portion of the query as well.
Here is the execution plan for the rewrite: Paste the Plan
And the results of the Statistics IO & Time:
The logical reads from the Customers table was cut by nearly a 1/3 and the CPU time was drastically cut, but the elapsed time is nearly double: new version was 1.2 seconds to .545 for the existing version.
I'm not an expert by any means and I am trying to learn, but the main differences I see is that the new version is performing a Key Lookup and the existing version is using Parallelism.
The advice or knowledge I'm hoping to gain here is which version of the stored procedure would give the best performance? And if the new version should be better, is there anything that could be done to make it run parallel so the elapsed time would be shorter?
Trying to clarify the question –
1) This maybe purely subjective and possibly frowned upon on this site, but based on the information provided; which procedure would you use to get the results to the end user the quickest? The proc with COALESCE/ISNULL functions in the WHERE clause the goes parallel or the revised procedure that has fewer logical reads but a greater elapsed time?
2) If we choose not to use dynamic SQL, what advice would you give to improve query performance for the revised procedure?
As I am typing the edits to try and clarify, I do see that Max has given some very useful information.
Just wanted to add, the Statistic Parser information was provided by this site.
Best Answer
Your first query plan shows parallelism, whereas your second query is purely serial; this is why the second version is showing longer "duration".
The key lookup operations could be prevented by a suitable covering index for the tables where the key lookup is occurring. The standard warning about not blindly creating indexes applies here - don't create duplicate indexes, and check to see if you can leverage an existing index by possibly adding an
include
clause. For instance, the key lookup on theCustomers
table is pulling these columns, which it couldn't get by scanning theIX_CustomersSocialSecurityNumber
index:If you added those columns to the index in an
INCLUDE
clause, that scan would not need to go back to the table to get those columns, making the output that much faster.Your query uses the "kitchen sink" pattern; i.e. this:
You can typically get much better query plans, customized for each variation, using dynamic SQL instead of the
@x IS NULL
piece. Pseudo-code would be:This allows the query optimizer to use column statistics in a far more effective manner, since it only needs to think about the columns presented in each unique
where
clause.Also of note, I see you're using
WITH (NOLOCK)
in an effort to prevent your query being affected by blocking. You may want to ensure you understand the effects of reading uncommitted rows inherent in theREAD UNCOMMITTED
isolation level used by theNOLOCK
hint. Aaron Bertrand has a great article about that hereI've noticed the plans show a couple of
computer scalar
operators where you're doing:Does your data really have blank space around the real content of
lastName
? If not, getting rid of those needless functions will really help the query processor provide better plans.As a way of showing how you might approach the kitchen sink problem, and strictly for learning purposes, consider the below code.
Some sample data:
A stored procedure to perform searches:
Some test searches:
The queries show in the "Messages" tab are:
Before you implement that code, you really need to read Erland Sommarskog's seminal work on dynamic SQL. He also has a great article about dynamic search which should help.