SlideShare a Scribd company logo
How to Build Fast
Multi-Parameter Stored Procs
About me
1999-05: dev, architect, DBA
2005-08: DBA, VM/SAN admin
2008-10: MCM, Quest Software
Ever since: consulting DBA
BrentOzar.com/go/dynamicsql
Agenda
What we’re trying to do
A few ways we shouldn’t do it, and why
The “right” way: sp_executesql
The drawbacks of the right way
Pro tips: troubleshooting and tuning
“I want a search page.”
Every user, ever
Q&A site: you ask, other people do your job
Whole database is available under Creative Commons
Download it free: BrentOzar.com/go/querystack
We’ll use the dbo.Users table
How big is our Users table today?
Stored proc in your resources
StackOverflow2010
Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures
Our proc has to look like this:
CREATE OR ALTER PROC dbo.usp_SearchUsers
@SearchDisplayName NVARCHAR(100) = NULL,
@SearchLocation NVARCHAR(100) = NULL,
@SearchReputation INT = NULL…
And folks want to pass in 1, 2, or 3 parameters, like just
DisplayName, OR
both Location and Reputation, and filter both.
But we wanna do less reads, so…
CREATE INDEX IX_DisplayName
ON dbo.Users(DisplayName);
CREATE INDEX IX_Location
ON dbo.Users(Location);
CREATE INDEX IX_Reputation
ON dbo.Users(Reputation);
Version 1:
the really bad idea
Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures
Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures
At first glance, it works.
Granted, the results aren’t accurate,
but it is willing to use indexes.
But there’s a catch.
2 different param sets
Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures
The London sort is spilling to disk
Because we underestimated rows
Not even close
Remember, 7748 pages in the table
We’re hitting parameter sniffing.
SQL Server compiles the entire plan
the first time it runs,
using the parameter values it was first run with.
So it’s optimizing the @SearchLocation branch with a null
@SearchLocation value.
This design has 3 big problems.
1. It produces the wrong results for param combos.
2. It’s a little TOO willing to use indexes,
even when they’re worse than a table scan.
3. It underestimates memory grants.
Version 2, OR:
accurate results
Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures
2 different param sets
Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures
SQL Server builds a plan for a name
But it may not ALWAYS have a name
Oddly, this performs fine
IF you don’t have any indexes.
Version 3:
COALESCE
Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures
Welcome to Scandinavia
Sure, key lookups too
Not great on reads, either
Version 4:
Dynamic SQL
Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures
2 different param sets
Logical reads look good, too
What if we run it for combos?
There’s a catch.
Clear the cache and run a few.
We bloat the plan cache a little.
The proc has its own entry, executed 6 times.
Each dynamic SQL string gets its own line.
But each dynamic plan is great* for that set of parameters!
* Not necessarily.
I got 99 problems plans
Each dynamic SQL plan has its own:
• Plan cache entry
• Memory grant
• Row estimations
• Parameter sniffing issues
Yes, I can still have param sniffing.
Chicago: big, but not huge.
London: big enough that a scan makes more sense.
If Chicago runs first…
We cache a plan with a seek.
And reuse it for London.
If London runs first…
We cache a plan with a scan.
And reuse it for Chicago
But the memory grant is too big.
Dynamic SQL
Gives you the luxury of multiple plans,
one for each set of parameters
But curses you with multiple plans,
each of which may have parameter sniffing issues.
There’s much more to learn.
Your demo scripts continue with pro tips for:
• Using comments inside the dynamic SQL string itself
for tracking down the source
• Formatting the strings with CR/LR
• Using debug variables to print at strategic times
• The perils of dynamic sorting
Download: BrentOzar.com/go/dynamicsql
Erland goes even deeper.
Alternate tables, forced recompilation, CLR…

More Related Content

Dynamic SQL: How to Build Fast Multi-Parameter Stored Procedures

  • 1. How to Build Fast Multi-Parameter Stored Procs
  • 2. About me 1999-05: dev, architect, DBA 2005-08: DBA, VM/SAN admin 2008-10: MCM, Quest Software Ever since: consulting DBA BrentOzar.com/go/dynamicsql
  • 3. Agenda What we’re trying to do A few ways we shouldn’t do it, and why The “right” way: sp_executesql The drawbacks of the right way Pro tips: troubleshooting and tuning
  • 4. “I want a search page.” Every user, ever
  • 5. Q&A site: you ask, other people do your job Whole database is available under Creative Commons Download it free: BrentOzar.com/go/querystack We’ll use the dbo.Users table
  • 6. How big is our Users table today? Stored proc in your resources StackOverflow2010
  • 8. Our proc has to look like this: CREATE OR ALTER PROC dbo.usp_SearchUsers @SearchDisplayName NVARCHAR(100) = NULL, @SearchLocation NVARCHAR(100) = NULL, @SearchReputation INT = NULL… And folks want to pass in 1, 2, or 3 parameters, like just DisplayName, OR both Location and Reputation, and filter both.
  • 9. But we wanna do less reads, so… CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName); CREATE INDEX IX_Location ON dbo.Users(Location); CREATE INDEX IX_Reputation ON dbo.Users(Reputation);
  • 13. At first glance, it works. Granted, the results aren’t accurate, but it is willing to use indexes. But there’s a catch.
  • 16. The London sort is spilling to disk
  • 17. Because we underestimated rows Not even close
  • 18. Remember, 7748 pages in the table
  • 19. We’re hitting parameter sniffing. SQL Server compiles the entire plan the first time it runs, using the parameter values it was first run with. So it’s optimizing the @SearchLocation branch with a null @SearchLocation value.
  • 20. This design has 3 big problems. 1. It produces the wrong results for param combos. 2. It’s a little TOO willing to use indexes, even when they’re worse than a table scan. 3. It underestimates memory grants.
  • 25. SQL Server builds a plan for a name But it may not ALWAYS have a name
  • 26. Oddly, this performs fine IF you don’t have any indexes.
  • 29. Welcome to Scandinavia Sure, key lookups too
  • 30. Not great on reads, either
  • 34. Logical reads look good, too
  • 35. What if we run it for combos?
  • 37. Clear the cache and run a few.
  • 38. We bloat the plan cache a little. The proc has its own entry, executed 6 times. Each dynamic SQL string gets its own line. But each dynamic plan is great* for that set of parameters! * Not necessarily.
  • 39. I got 99 problems plans Each dynamic SQL plan has its own: • Plan cache entry • Memory grant • Row estimations • Parameter sniffing issues
  • 40. Yes, I can still have param sniffing. Chicago: big, but not huge. London: big enough that a scan makes more sense.
  • 41. If Chicago runs first… We cache a plan with a seek. And reuse it for London.
  • 42. If London runs first… We cache a plan with a scan. And reuse it for Chicago But the memory grant is too big.
  • 43. Dynamic SQL Gives you the luxury of multiple plans, one for each set of parameters But curses you with multiple plans, each of which may have parameter sniffing issues.
  • 44. There’s much more to learn. Your demo scripts continue with pro tips for: • Using comments inside the dynamic SQL string itself for tracking down the source • Formatting the strings with CR/LR • Using debug variables to print at strategic times • The perils of dynamic sorting Download: BrentOzar.com/go/dynamicsql
  • 45. Erland goes even deeper. Alternate tables, forced recompilation, CLR…