Aug 03 2011

All About Execution Plans

If you’re attending the PASS Summit this October, I’d like to make a suggestion. The Summit itself is only three days long, Wednesday to Friday. But, if you have to travel any distance to get there, you’re going to miss work on Tuesday as well. Why not take a whole week away and spend Monday with Gail Shaw (blog|twitter) and me?

The reason I ask is because Gail and I are putting together 7 hours of information all about execution plans. We’re going to be presenting this information on Monday at the Summit as a pre-conference seminar. Yes, you’ll have to pay extra to attend this session. But if you register for the Summit now, there’s still a discount, which you can put towards to the seminar. And, seriously now, didn’t you want to learn more about execution plans? This is your chance.

Gail is an acknowledged SQL Server expert who writes and speaks regularly about execution plans and query performance tuning. If you read this blog at all, you might notice the occasional post about execution plans. Plus there’s the book on execution plans and the one on query tuning . Come on! You really do want to attend our session.

Even if you’ve already registered for the PASS Summit, you can still add a pre-conference seminar to your registration. If you don’t like learning about execution plans, there are lots of other good seminars to pick from (I’d recommend considering Rob Farley’s (blog|twitter) session even though he can be slightly backwards when it comes to execution plans [kidding]).

Jan 21 2011

Deprecation, Trace and Execution Plans

chopping_blockAs I’m sure you know, Microsoft occasionally changes it’s mind. Or, it makes bad decisions and then rectifies them. Or, it even reinforces bad decisions. Regardless of the purpose, the means by which these changes are implemented when they involve taking things away is deprecation. Usually in SQL Server the deprecation process is supposed to be over three releases. So while seeing something on the deprecation list can be cause for concern if it’s something you like, you certainly don’t need to panic.

I’ve finally had a chance to start working with Denali and the place that concerned me most was in the areas of deprecation. I want to make sure that when I suggest a particular approach, that the approach isn’t going to disappear in a version or two.Which brings us to my personal little point of interest, execution plans.

When I originally wrote my book on execution plans (free download by the way in case you don’t have a copy), I talked about text execution plans as being on the chopping block. They still are.

I was reading through the online Denali documentation when I ran across this list of deprecated Trace Events (I was trying to see if Trace/Profiler was on the deprecation list, more on that in later posts). You’ll note that the deprecated events are the text plan events. Now, deprecation takes place over three versions, right? Clicking back through the different versions I see that these events made the list originally in SQL Server 2005. The documentation for Denali is clearly marked as “Preview Only” and subject to change. But, based on their own approach, in theory, these events are slated for removal in this version. Microsoft can change their minds and extend it a version (does R2 really count as a version?) or update the docs to show this as removed functionality, either way, if you’re dependent on the events listed, I’d start getting more comfortable with using XML plans.

DeprecationTo attempt to discern Microsoft’s plans for the trace events, I tried something.. a little weird. There are trace events that will tell you the status of things on the deprecation list, Deprecation: Deprecation Announcement, Deprecation: Final Support. The Final Support event is supposed to show those things that are currently on the chopping block, not just at some future point. What I did was set up a trace to capture showplan_all and then set up a trace to capture both Deprecation events. I didn’t get anything. Sorry. That just means we’ll have to work from the documentation, which tells us to prepare for those events to be removed from future versions.

I also tried out the showplan_all itself to see if that would pop up in the Deprecation trace. It didn’t. I don’t know if I should trust the Deprecation trace or the documentation on these things. If I find out one way or the other, I’ll put it up in a blog post.

Nov 18 2010

Reason for Early Termination of Statement

Wouldn’t you like to know why the optimizer stopped tuning the execution plan you’re looking at? It’s actually possible and simple to get this information. I talked about this a little more than a year ago, but I left out some information that might be useful.

Let’s take a very simple query:

SELECT * FROM Person.Address AS a;

This generates a simple execution plan, in fact a trivial plan:

Trivial Execution Plan

I know that this is a trivial plan one of two ways. The hard way is to look at the XML (BTW, this is the type of thing you can’t see it in STATISTICS PROFILE, which I understand a lot of people are using). Right at the top is the SELECT element. You can see the value for StatementOptmLevel property is equal to “TRIVIAL.” But reading XML is a pain. Right clicking on the SELECT operator above and selecting Properties will result in this property sheet:

Trivial Plan Properties Sheet

If you look at the properties here, just six up from the bottom is “Optimization Level,” the human readable equivalent to StatementOptmLevel. You can see that it’s set to TRIVIAL.

I know exactly what you’re thinking, “Hey, what the heck happened to early termination & stuff?”

Hang on. I’ll get you there.  The fact is, not every plan has a reason for early termination, like the one above. When a plan is identified as TRIVIAL, it doesn’t go through the optimizer at all. This is why it doesn’t display a reason for early termination. It didn’t terminate, it just stopped the process. The trick is, to get a more complicated query so that we get a more complicated plan:

SELECT p.LastName + ', ' + p.FirstName AS FullName,
a.AddressLine1,
a.City,
a.PostalCode
FROM Person.Address AS a
JOIN Person.BusinessEntityAddress AS bea
ON a.AddressID = bea.AddressID
JOIN Person.Person AS p
ON bea.BusinessEntityID = p.BusinessEntityID
WHERE a.AddressID = 252

This query will not return a trivial plan because the plan, despite the simplicity of the query, is too complex, due to the joins, etc. Regardless of the details about the trivial plan, the interesting point here is to be found, again, in the properties of the SELECT operator:

With this set of properties, look first at the “Optimization Level.” Instead of TRIVIAL, we have a FULL optimization, something to shoot for. Looking down near the bottom of the properties you can see “Reason For Early Termination.” The value is “Good Enough Plan Found.” Which means that the optimizer feels, based on the statistics, that it has found a pretty good plan, not necessarily the best possible, plan, but a plan that is good enough. Which is exactly what the optimizer is supposed to do. You can also see the termination reason in the XML for the plan in the property StatementOptmEarlyAbortReason.

The trouble comes in when the queries are truly complex, nested views where views join to views are a good example. With a view, the optimizer can look at the query being run against it and make determinations as to whether all the tables are needed to satisfy the query. It’s possible that some tables are not needed. But when you start nesting views and joining views to views, well, things change. This is just an example (yes, I’m using *, I wouldn’t do this in production, I just need to get an ugly plan, and this is ugly) that uses three views from AdventureWorks2008R2:

SELECT *
FROM HumanResources.vEmployee AS ve
JOIN Sales.vSalesPerson AS vsp
ON ve.BusinessEntityID = vsp.BusinessEntityID
JOIN Sales.vSalesPersonSalesByFiscalYears AS vspsbfy
ON vspsbfy.SalesPersonID = vsp.BusinessEntityID

This, seemingly simple, query produces a pretty heinous execution plan. I’ve zoomed and shrunk it to see it all at once:

And while this seems quite complex, the fact is, these views, and this execution plan, are relatively well written. My query against them is fairly out of line, but imagine what the plan might look like if someone who would write such an out of line query also wrote the views. Yes, these execution plans can get quite ugly. If we go on to examine the properties on the SELECT statement we’ll see something different:

This time you see that the “Optimization Level” is FULL, but the “Reason For Early Termination” is “Time Out.” This means that the optimizer, which doesn’t really measure time, but the number of attempts it makes at finding an optimal plan, has run through all it’s attempts and arrived at something less than “good enough.” This time it simply stopped trying. In this case, it takes the plan that is currently the best, which might be a good plan… or it might not be, and applies that plan to the query. While it’s guaranteed to return the data correctly, it may or may not return the data efficiently, and looking at the execution plan in this, that’s unlikely.

Why is this? Well, the easiest place to start is pretty simple, look at the number of tables in that execution plan. If you take into account the data that could meet the criteria of the query, in this case, most of it, and the number of tables, you can see why the optimizer might be taxed. After that, things get complicated. You have to begin to look at how you’re putting your query together, the types of functions and methods you’re using and, in general, the overall complexity of the process. My respect for the developers who have created the optimizer is just about boundless. These are some amazing people to have put all this together. But, the problem they’re solving is incredibly difficult, and they are not working miracles (even though it sometimes feels that way). The best thing you can do is to understand how the optimizer works and try to help it along in places where it might need it.

There is one more reason for early termination of the optimizer, and you may see it. I can’t reproduce it at will in a sample database, but it’s “Memory Limit Exceeded.” That one is largely self-explanatory and, unlike the other two values, it represents a serious problem. In this case, you need to do something about the available memory on your server. You can increase memory or decrease memory pressure by taking load off the server. Those are really your only options. You’ll still get a functional execution plan on this, but, like the timeout, it’s very likely this is not an optimal plan and certainly not “good enough.”

Oct 04 2010

How to Tell if Execution Plans are Reused

I try to watch the search phrases that point people to the blog because sometimes, you get a sense of what problems people are running into. The latest question or phrase I’ve seen a lot lately is along the lines of “how do you know if an execution plan is being reused.”

Since compiling an execution plan can be an extremely expensive operation, it’s worth your time to understand how well a given plan is getting reused. If you’ve seen me present, I’ll frequently talk about the application that had a query with an 86 table join. Recompiles on that thing were frequent and extremely costly. The only good news was, they were recompiles. If we weren’t getting plan reuse it would have been an even worse system than it was.

There are a number of ways you can see if a plan is being reused. One of the easiest is to hit the DMOs that look into the plan cache. You can look at either sys.dm_exec_procedure_stats or sys.dm_exec_query_stats. These DMOs are somewhat different and somewhat the same. Actually, they sort of cross each other. For sys.dm_exec_procedure_stats, you get aggregate information about stored procedures. For sys.dm_exec_query_stats you get aggregate information about queries, which, may be run within stored procedures. So either or both could be useful depending on what you’re attempting to measure. However, both return a very useful counter, execution_count. Quite simply, that tells you that the plan, whether for the statement or the procedure, is being reused. To get maximum gain out using either of these, you’ll want to combine them with other DMOs. Something along these lines can show you the plans for procedures that have been running against a system, ordered by the number of times they’ve been reused:

SELECT deps.execution_count ,
OBJECT_NAME(deps.object_id, deps.database_id) 'Procedure' ,
deqp.query_plan
FROM sys.dm_exec_procedure_stats AS deps
CROSS APPLY sys.dm_exec_query_plan(deps.plan_handle) AS deqp
ORDER BY deps.execution_count DESC

The only thing wrong with using the DMO like this is that you can only see what’s currently in cache. This means no history, depending on how volatile the cache is on your system.

Another way to tell if a plan is being reused, is to set up a server side trace and capture the event for the SP:CacheMiss or SP:CacheHit. This is pretty straight forward. If the query was not in, it generates a miss event. If it was there, it generates a hit. But, this has to be running in order for you to know if you had a hit or a miss. The good news is, if it’s running, you’ve got historical information since this captures the event as it occurs.

You can also catch recompile events using SQL:StmtRecompile. Yes, you can get SP:Recompile if you’re only ever dealing with procedures, but if you’ve got any kind of ad-hoc querying going on in the system or triggers, you’ll miss recompile events. Basically, because recompiles are at the statement level starting in 2005, BOL recommends only using SQL:StmtRcompile.

That’s pretty much it. Using these methods in the appropriate place will let you know if the plan is being reused or not.

Sep 27 2010

Parameter Name Size And Performance

I saw an odd statement the other day, “The size of the name of the parameter does not affect performance.” My first thought was, “Well, duh!” But then, I had one of those, “Ah, but are you sure” thoughts. And you know what, I wasn’t sure.

If size of the parameter name did affect performance, I figured, the one sure place where that would be evident is in the size of the execution plan. Right? I mean, if there was an impact on memory, and hence on performance, that’s probably where you’d see evidence of it. I wrote two queries:

 DECLARE @ThisIsAVeryVeryLongParameterNameThatIsTrulyRidiculousButItIllustratesThePointThatParameterLengthDoesNotAffectPerformance int
SET @ThisIsAVeryVeryLongParameterNameThatIsTrulyRidiculousButItIllustratesThePointThatParameterLengthDoesNotAffectPerformance = 572
SELECT soh.SalesOrderID
,sod.SalesOrderDetailID
FROM Sales.SalesOrderHeader AS soh
JOIN Sales.SalesOrderDetail AS sod
ON soh.SalesOrderID = sod.SalesOrderID
WHERE soh.SalesOrderID = @ThisIsAVeryVeryLongParameterNameThatIsTrulyRidiculousButItIllustratesThePointThatParameterLengthDoesNotAffectPerformance

DECLARE @v int
SET @v = 572
SELECT soh.SalesOrderID
,sod.SalesOrderDetailID
FROM Sales.SalesOrderHeader AS soh
JOIN Sales.SalesOrderDetail AS sod
ON soh.SalesOrderID = sod.SalesOrderID
WHERE soh.SalesOrderID = @v

If you run this against AdventureWorks2008R2 you’ll get two distinct, but identical, execution plans:

You can see that they look identical, but how do I know they’re distinct? If you run this query:

 SELECT deqs.creation_time,
deqs.query_hash,
deqs.query_plan_hash
FROM sys.dm_exec_query_stats AS deqs
CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) AS deqp
CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS dest
WHERE dest.text LIKE '%SELECT soh.SalesOrderID%'

You’ll get this back as a result:

creation_time                               query_hash                                 query_plan_hash
2010-09-23 18:18:09.347      0x8D0FB9D524B8DD4D       0x13707445560737BA
2010-09-23 18:18:16.223       0x8D0FB9D524B8DD4D       0x13707445560737BA

Two distinct queries, but with identical hash values, so the plans generated are nearly the same, but clearly different, due to the fact that they were built with different parameters, including the monster name. So, how to see if there is a difference in the plan generated that could affect performance? How about the execution plan properties. First, the property sheet for the SELECT operator for the query with the long parameter name:

Of particular note is the Cache Plan Size. Let’s compare it to the same property sheet for the small parameter name:

If you compare the two, you’ll see that they’re the same. In fact, if you look at almost all the values, you’ll see that the Compile CPU, Compile Memory, and Compile Time are all identical. Based on all this information, I have to conclude that no, the size of the name of the parameter doesn’t affect performance, positively or negatively. But why?

I’m actually not 100% sure, but based on some things I know, here’s what I think. The Algebrizer within the Query Optimizer breaks down all the objects referred to within a query plan. It assigns them all values and identifiers for that plan, part of getting the information together to feed the plan into the mathematical part of the Optimizer. I’ll bet it just assigns values to parameters that are the same type of value, if not the same exact value, and that value is the same size from one execution plan to the next.

This means that you don’t save memory by assigning parameters @a, @b, @c when in fact you mean @ReferenceCount, @MaxRetries, @BeginDate. Do I think you should put in parameters of the silly length I put in before? No, of course, not, because it makes the TSQL code less clear. But, so does putting in equally silly, short, parameter names.

Don’t make your TSQL code hard to read. It doesn’t help performance.

Aug 11 2010

Viva Las Vegas!

I won’t be going to Las Vegas, but I will be presenting to the Las Vegas SQL Server Users Group, S3OLV. The sesssion will be “Introduction to Execution Plans.” Please swing by if you’re in the area.

I’m not sure if they’ll make the LiveMeeting available to the public or if they’ll record the session. But if they do, please attend that way too.

Oct 28 2009

I Started a Fight

I had no idea, but evidently I started a bit of a donny-brook. Cool!

Oct 06 2009

Hey, ho, where'd you go…

Ohio?

I’m presenting on Thursday to the Columbus SQL Server Users Group, home of the newly minted MVP, Jeremiah Peschka (congrats again). It’s to be a virtual presentation through live meeting. The topic is a Understanding Execution Plans. This time I hope that one of us remembers to hit the record button unlike last time when I did this presentation for the PASS AppDev Virtual Chapter. If you’re in the area, come on down and meet me, virtually. Show runs from 6:30-8:30, although all that time is not my presentation, so I suspect Jeremiah has some more fun planned.

Sep 22 2009

Gila Monster on Estimated vs. Actual Row Counts

I don’t generally do lots of blog aggregation and cross post linking & stuff. It’s just not something I’m that into. However, this time is an exception. Gail Shaw, Gila Monster to those who hang on out SQL Server Central, has posted an excellent explanation of times when Estimated and Actual row counts vary for a reason. I’m one of those who emphasises that differences between estimated & actual is an indication of… something. It could be out of date or missing statistics or it could be caused by query language like multi-statement table valued functions, but it’s usually and indication of a problem. Except when it’s not. Read Gail’s explanation for more.

Sep 15 2009

No Join Predicate

You could be looking at an execution plan on a query and see this message: Warning, No Join Predicate. With a very few exceptions (very few), that’s not a warning, like “Mind the gap” or “Your Mileage May Vary” or “Never fight a land war in Asia.” No. It’s a screaming shout saying “BY ALL THE GODS IN VALHALA, YOU DON’T HAVE ANY JOIN PREDICATES IN THIS QUERY!” Seriously, that’s exactly what it says.

But, you might be asking yourself, that’s what it says, but what does it mean? Please allow me to explain. The tables in your system, whether it’s a classic BI star schema, or a highly normalized OLTP system, or even (shudder) ORM objects, are related to one another. Usually they’re related through the use of primary and foreign keys. The primary key uniquely identifies a row, like a particular value in a dimension table. This then is related to some number of rows in another table, like in a fact table, through the foreign key. The foreign key acts to protect the integrity of the data, ensuring that if you insert something that’s supposed to relate to another row, then that other row really exists and if you try to delete that other row, the foreign key will remind you that, hey, that row has things that depend on it existing in another table. All of this can be quite complicated or quite simple. Heck, you may be using an ORM system and don’t have a single foreign key in site.

But, when you go to retrieve data from two tables that are related in a TSQL query, you need to define that relationship in the TSQL itself. SQL Server 2008 relies on what was defined as the ANSI standard back in 1992. But, if you learned your SQL coding skills back in the day or on Oracle, you may be used to simply listing tables and then defining their relationships through the WHERE clause. Just a side note, unless you learned your SQL skills in Oracle, if you were born AFTER the 1992 standard was enacted, you don’t have an excuse. Anyway, the old way that code looked like was this:

SELECT…
FROM TableA AS a, TableB as b, TableC as c
WHERE a.MyID = b.MyID
AND b.YourID *= c.YourID
AND….

What you have there is an inner join, or in the old parlance, an equi-join because of the use of the equals sign, between TableA and TableB and an left outer join, defined through the use of the ‘*’, between TableB and TableC. Any other criteria that defined the WHERE clause then followed.

Flat out, in SQL Server 2008, you can’t do this any more. It will not support that style of outer join. The new fangled method, remember, new as of 1992, is this:

SELECT …
FROM TableA as a
INNER JOIN TableB as b
ON a.MyID = b.MyID
LEFT JOIN TableC as c
ON b.YourID = c.YourID
WHERE….

This forces you to separate the relations between the tables as defined by the JOIN’s and the filtering methods supplied through the WHERE clause. And yes, it’s a lot more complicated than I’m making it out to be, but I’m trying to establish the ground rules here. Where was I? Oh yeah, missing join criteria.

What happens is, someone that’s used to writing things in the ANSI ’89 syntax can either forget to define the JOIN criteria or accidently remove it while editing or something. Regardless of how it happened, you’ve ended up with what is known as a cartesian product, shortest possible explanation, everything joined on everything else. For example, here’s a real query against the AdvenureWorks2008 database:

SELECT *
FROM Sales.SalesOrderHeader AS soh
,Sales.SalesOrderDetail AS sod
,Production.Product AS p
WHERE soh.SalesOrderID = 43659

That query does not define join criteria between any of the tables involved. This results in the following estimated execution plan (estimated because I got sick of waiting for the query to finish):

MissingJoin

If you look at the final Nested Loop operation right before the SELECT statement operator, you’ll notice there is a little exclamation point inside a yellow triangle. This is an indication of a warning in your query.  You can hover over the operator to get the tool tip, which looks like this:

NoJoinPredicate

You can see at the bottom the warning that no join predicate is in use. If you constructed the query appropriately it would look more like this:

SELECT *
FROM Sales.SalesOrderHeader AS soh
JOIN Sales.SalesOrderDetail AS sod
ON soh.SalesOrderID = sod.SalesOrderID
JOIN Production.Product AS p
ON sod.ProductId = p.ProductID
WHERE soh.SalesOrderID = 43659;

Which, instead of returning 800,000 rows before I interrupted it returned only 12 rows and had a very clean execution plan:

Join

I would never consider “No Join Predicate” as a warning. It’s a blaring klaxon of a problem and you need to do something about it, right away.