SQL Server Performance

  • Home
  • Articles
  • Forums
  • Tips
  • Training
  • FAQ's
  • Blogs
  • Software
  • Books
  • About Us
RSS Feeds
Sign in | Join


Article Topics

All Articles
Performance Tuning
Audit
Business Intelligence
Clustering
Reporting Services
SQL Azure
Developer
General DBA
ASP.NET / ADO.NET
SQL Azure

USEFUL SITES :

ASP.NET Tutorials
Windows and SQL Azure Tutorials
Cloud Hosting Magazine
SharePoint Tutorials
Windows Server Help

Write for Us

Share your SQL Server knowledge with others and raise your profile in the community More...
Latest Articles

A High Level Comparison Between Oracle and SQL Server - Part ...
A High Level Comparison Between Oracle and SQL Server - Part ...
A High Level Comparison Between Oracle and SQL Server - Part ...
A High Level Comparison Between Oracle and SQL Server

More     
 
Latest FAQ's

Add Node to A SQL Server failover Cluster failed with invalid ...
SQL Server Destination remote server error
Setting Up Data And Log Files For SQL Server
Will Check Constraints Improve Database Performance?

More     
   
Latest Software Reviews

dbForge Review
Spotlight on ApexSQL Diff - Server-based database comparison tool ...
Spotlight on ApexSQL Data Diff - Server-based database comparison tool ...
Spotlight on ApexSQL Doc 2008

More     

articles >> performance tuning >> Breaking Down Complex Execution Plans

Breaking Down Complex Execution Plans

By : Grant Fritchey
Jun 21, 2008

As a DBA, whether working as a full time employee of a company or as a consultant, you’re often handed stored procedures or queries long after they’ve been “finished.” These finished queries are frequently enormous, free-flowing affairs with between 50 and 500 statements joining up to 80 tables, views, derived tables and functions. Together they arrive at some set of results. We’re brought in because the finished query runs slowly or is causing deadlocks or blocks or takes forever to recompile, whatever is wrong with it. Now you have to make sense out of this incredibly complex query. How do you break it down?

The short answer; just like eating an elephant, one bite at a time. The long answer...There are two levels to the complexity that you have to address. The first level of complexity is the shear number of statements that have to be dealt with. Which ones are causing the majority of the load? Which ones can I safely ignore. The second level of complexity is an execution plan that looks something like this:


Figure 1

Which operations are causing the bottleneck? Where do I spend my time?

This article will try to provide you with a few tools so that you can more readily answer these questions for yourself. I’m going to introduce a number of options and tricks using SQL Server Management Studio, the query window, execution plans and the SQL Server Profier. However, this isn’t meant to be a tutorial on any of these subjects. If you’re just getting started, get familiar with those topics first then come back and tackle that complex query plan using these hints and tips.

Statements, Statements Everywhere
The incredibly extreme number of statements that some of the people writing stored procedures are able to cram in boggles the mind. Three different approaches deal with this glut of statements with varying ease of use and accuracy of measure. First, using the graphical execution plan’s “Query cost (relative to the batch)” is easy and immediately available as a part of displaying the execution plan. It also tends to be somewhat inaccurate. So inaccurate that many DBA’s won’t use it at all while others use it merely as an easy starting point. Next, the Advanced set of Query Options allow you to capture statement time and I/O. These are very accurate, but sometimes difficult to interpret accurately. Finally, Profiler provides a mechanism for capturing individual statements within a query or procedure. While this isn’t as convenient to the execution plan as the “Query Cost,” it is as easy to read. It’s just as accurate as capturing time and I/O but much easier to interpret.

“Query Cost”
Most of the time, the values displayed in “Query cost (relative to the batch):” accurately display the relative cost of each statement within the greater context of the batch. The batch can either be a query or a stored procedure. Simply capturing an Execution Plan, Estimated or Actual, through the GUI in Management Studio will automatically list each statement as a separate “query.” They will be numbered in the order in which they were called and an estimate of their cost relative to the batch will be displayed:


Figure 2

This can make it very quick & simple to determine which statement costs the most. The problem is, this cost isn’t based on the actual I/O or time that the statement took to run. Instead it’s based on the optimizer’s Estimated Cost for the statement. These estimates can be off for a number of reasons, including, but not limited to, out of date statistics, missing statistics, table variables, recursive common table expressions, and multi-statement table valued functions, just to name a few. Because these things can skew the Estimated Cost of the statement, they can mask it’s actual cost within the execution of the greater batch.

Here’s an example that’s just about as extreme a difference as possible. Jeff Moden created this example and sent it to me for the express purpose of alerting people to the dangers of using the Query Cost in graphical execution plans. It requires a tally table, also known as a table of numbers. This is simply a table with one column and a list of numbers 1 to n. Included is a simple script to set up a tally table for your test:

CREATE TABLE Tally (Num INT NOT NULL)

DECLARE @i INT -–NOTE: This is a simple, not efficient, load
SET @i = 1
WHILE @i<10000
BEGIN
 INSERT INTO [Tally] (
  [Num]
 ) VALUES (
  /* Num - INT */ @i )
 SET @i = @i + 1
END
GO

Run the following query with “Include Actual Execution Plan” turned on:

SET NOCOUNT ON
--=======================================================================================
-- Recursive method shown by (Name with-held)
--=======================================================================================
  PRINT '========== Recursive method =========='
--===== Turn on some performance counters ===============================================
    SET STATISTICS IO ON
    SET STATISTICS TIME ON
DECLARE @BitBucket DATETIME --Holds display output so display times aren't measured.

--===== Execute the code being tested ===================================================
DECLARE @DateVal DATETIME
    SET @DateVal = '2008-01-01'

;with mycte as
     (
       select @DateVal AS DateVal
       union all
       select DateVal + 1
         from    mycte   
         where   DateVal + 1 < DATEADD(yy, 5, @DateVal)
     )
select @BitBucket = d.dateval
from mycte d
OPTION (MAXRECURSION 0)

--===== Turn off the performance counters and print a separator =========================
    SET STATISTICS TIME OFF
    SET STATISTICS IO OFF
  PRINT REPLICATE('=',90)
GO

--=======================================================================================
-- Tally table method by Jeff Moden
--=======================================================================================
  PRINT '========== Tally table method =========='
--===== Turn on some performance counters ===============================================
    SET STATISTICS IO ON
    SET STATISTICS TIME ON
DECLARE @BitBucket DATETIME --Holds display output so display times aren't measured.

--===== Execute the code being tested ===================================================
DECLARE @StartDate AS DATETIME
    SET @StartDate = '2008-01-01'

SELECT TOP (DATEDIFF(dd,@StartDate,DATEADD(yy,5,@StartDate)))
        @BitBucket = @StartDate-1+t.Num
   FROM Tally t
  ORDER BY Num

--===== Turn off the performance counters and print a separator =========================
    SET STATISTICS TIME OFF
    SET STATISTICS IO OFF
  PRINT REPLICATE('=',90)
GO

You should see something like this as a result:



Figure 3

As you can see, the “Query Cost (relative to the batch):” for the first query is showing up as 0% while the second query is 100% of the cost. But, if we look at the Messages window:



Figure 4


    Next Page>>    








C# Help and Tutorials | PHP MySQL Tutorial | Sharepoint Tutorial | Azure Tutorial | Cloud Hosting Magazine | ASP.NET Tutorials | Windows Server Help | Windows Phone Pro | Silverlight Ace | Visual Studio Tutorials | Home | Peformance Articles | Audit Articles | Business Intelligence Articles | Clustering Articles | Developer Articles | Reporting Services Articles | DBA Articles | ASP.NET / ADO.NET Articles | SQL Server Training Videos | DBA FAQ's | Developer Peformance FAQ's | DBA Peformance FAQ's | Developer FAQ's | Clustering FAQ's | Error Messages | Audit Tool Reviews | Backup Tool Reviews | Coding Tool Reviews | Compare Tool Reviews | Documentation Tool Reviews | Design Tool Reviews | Monitoring Tool Reviews | Log Tool Reviews | Reporting Tool Reviews | Clustering Tool Reviews | Security Tool Reviews | Change Management Tool Reviews | Remote Access Tool Reviews | Book Reviews | Security Tool Reviews | ADO.NET / ASP.NET | Administration | Analysis/OLAP Services | Application Development | Configuration | Components | ETL | Hardware | High Availability | Hints | Index | Misc | Operating Systems | Performance Tuning | Replication | T-SQL | Views


              © 2010 Jude O'Kelly. All rights reserved