• subscribe
July 19, 2005 12:00 AM

Conditional Recompile

Strike a balance between performance and security
SQL Server Pro
InstantDoc ID #46643
Downloads
46643.zip

Another solution involves creating two new stored procedures that an outer procedure calls, depending on which plan is best. For the examples so far, it would be difficult to predict without running the query which parameters will require which plan because each customerID value occurs a different number of times. For other types of queries, there might be a solution. If you had a range query where the parameter was the endpoint of the range, you might be able to predict that endpoints at the low end of the range would return only a few rows. Thus, using the nonclustered index would be a good plan, but for endpoints higher in the range, a different plan would be better.

The code in Listing 4 rewrites the getorders procedure again and creates two sub-procedures. SQL Server now calls the outer procedure, getorders, when you want to return all the rows that have a customerID value less than the input parameter. For this small amount of data in the Orders table, SQL Server could get faster performance with any value greater than or equal to ANTON by performing a table scan. Note that the two nested procedures are identical. However, the outer procedure calls the first one with a very selective parameter and the second with a not-so-selective parameter, so the plan for the second one will use a table scan. You can test the new procedure with the following batch, keeping in mind that you're now returning all orders for customers whose ID is less than the parameter:

EXEC getorders 'ALFKI'
EXEC getorders 'RANCH'

Again, you get an appropriate plan for both parameters, as you can see by the number of logical reads.

This solution isn't perfect, either. It's useful only when you can guess up front which actual parameters SQL Server will treat one way and which it will treat another way, but it doesn't account for changing the plan when the data distribution changes. If you perform updates, inserts, and deletes to the Orders table and change the data distribution, so that now any customerID values less than BINGO should be treated as highly selective, you'll have to rewrite the getorders procedure. You have to constantly monitor performance and retest the procedures to make sure that you're still getting the best possible plan as the data changes.

Nobody's Perfect
How do you get the best of both worlds? You don't want to recompile when the existing plan is good, but you need to be able to recompile (and reoptimize) when the existing plan isn't so good. And, you don't want to have to recompile more of the procedure than necessary. There's no perfect solution. When SQL Server knows that an existing plan might not be efficient because of changes to metadata or statistics, it can automatically recompile. But if an existing plan gives bad performance just because you're using a different parameter, SQL Server can know it needs a different plan only by taking the time to reoptimize. The more you know about how to use T-SQL to your advantage for tuning and optimization, the better performance you'll achieve.

Related Reading
SQL Server Magazine articles:
WILLIAM SHELDON
"Preventing SQL Injection Attack," August 2004, InstantDoc ID 43012
MICHAEL OTEY
SELECT TOP(X), "Injection Protection," March 2004, InstantDoc ID 41502
UMACHANDAR JAYACHANDRAN
Reader Challenge: "Protecting Against SQL Injection," April 6, 2004, InstantDoc ID 42216
CHIP ANDREWS
"Rest Secure," August 2001, InstantDoc ID 21268

Other sources:
ERLAND SOMMARSKOG
"The Curse and Blessings of Dynamic SQL," http://www.sommarskog.se/dynamic_sql.html
MERKIN
"Introduction to Dynamic SQL," June 2001, http://www.sqlteam.com/item.asp?ItemID=4599





ARTICLE TOOLS

Comments
  • belwar
    7 years ago
    Aug 24, 2005

    Filled a big void in my expectations. After reading Reusing Query Plans in July, I felt ripped off, like I'd been left hanging (ever experienced this sub-optimal plan caching problem?...yeah, sucks doesn't it?). I was left asking myself "what about dynamic SQL? would that do the trick? if not, then what's the answer? there must be some solution, otherwise why write an article about it?" When I read part 2 (actually I guess this would be part 3) today I was breathing a sigh of relief by the end of the first page - "ahhh...she's addressing dynamic SQL...my life isn't meaningless after all...some closure at last". Great article.

    I was left wondering about SQL 2005 however. Have the Microsoft guys done anything, other than statement-level recompiles, to address this problem? Also, do triggers (SQL 2000 & 2005) suffer from this same compiled plan problem? They do cache plans don't they (there are Cache Manager perfmon counters for trigger plans)? Can trigger plan recompiles be forced? When are they automatically recompiled? etc., etc. There's probably another article in the making there somewhere...

You must log on before posting a comment.

Are you a new visitor? Register Here