solving sql server problems for millions of dbas and developers since 2006



SQL Server DBA Tips SQL Server Developer Tips SQL Server Business Intelligence Tips SQL Server Career Tips SQL Server Tip Categories SQL Server Tutorials SQL Server Webcasts SQL Server Whitepapers SQL Server Tools SQL Server Questions and Answers MSSQLTips Authors About MSSQLTips SQL Server User Groups MSSLQTips Giveaways MSSQLTips Advertising Options

MSSQLTips Facebook Page MSSQLTips LinkedIn Page MSSQLTips RSS Feed MSSQLTips Twitter Page MSSQLTips Google+ Page





Collecting and Storing Poor Performing SQL Server Queries for Analysis

By: | Read Comments (10) | Print

Ben has been a SQL Server and Oracle DBA for over 10 years and focuses on performance tuning.

Related Tips: More

Problem

In an ideal world all of our queries would be optimized before they ever make it to a production SQL Server environment, but this is not always the case. Smaller data sets, different hardware, schema differences, etc. all effect the way our queries perform. This tip will look at a method of automatically collecting and storing poor performing SQL statements so they can be analyzed at a later date.

Solution

With the new Dynamic Management Views and functions available starting in SQL Server 2005, capturing information regarding the performance of you SQL queries is a pretty straightforward task. The following view and functions give you all the information you need to determine how the SQL in you cache is performing:

Using the view and functions above we can create a query that will pull out all the SQL queries that are currently in the cache. Along with the query text and plan we can also extract some important statistics on the performance of the query as well as the resources used during execution. Here is the query:

SELECT TOP 20
    GETDATE() AS "Collection Date",
    qs.execution_count AS "Execution Count",
    SUBSTRING(qt.text,qs.statement_start_offset/2 +1, 
                 (CASE WHEN qs.statement_end_offset = -1 
                       THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 
                       ELSE qs.statement_end_offset END -
                            qs.statement_start_offset
                 )/2
             ) AS "Query Text", 
     DB_NAME(qt.dbid) AS "DB Name",
     qs.total_worker_time AS "Total CPU Time",
     qs.total_worker_time/qs.execution_count AS "Avg CPU Time (ms)",     
     qs.total_physical_reads AS "Total Physical Reads",
     qs.total_physical_reads/qs.execution_count AS "Avg Physical Reads",
     qs.total_logical_reads AS "Total Logical Reads",
     qs.total_logical_reads/qs.execution_count AS "Avg Logical Reads",
     qs.total_logical_writes AS "Total Logical Writes",
     qs.total_logical_writes/qs.execution_count AS "Avg Logical Writes",
     qs.total_elapsed_time AS "Total Duration",
     qs.total_elapsed_time/qs.execution_count AS "Avg Duration (ms)",
     qp.query_plan AS "Plan"
FROM sys.dm_exec_query_stats AS qs 
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt 
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE 
     qs.execution_count > 50 OR
     qs.total_worker_time/qs.execution_count > 100 OR
     qs.total_physical_reads/qs.execution_count > 1000 OR
     qs.total_logical_reads/qs.execution_count > 1000 OR
     qs.total_logical_writes/qs.execution_count > 1000 OR
     qs.total_elapsed_time/qs.execution_count > 1000
ORDER BY 
     qs.execution_count DESC,
     qs.total_elapsed_time/qs.execution_count DESC,
     qs.total_worker_time/qs.execution_count DESC,
     qs.total_physical_reads/qs.execution_count DESC,
     qs.total_logical_reads/qs.execution_count DESC,
     qs.total_logical_writes/qs.execution_count DESC

This query can be easily modified to capture something specific if you are looking to solve a particular problem. For example, if you are currently experiencing an issue with CPU on you SQL instance you could alter the WHERE clause and only capture SQL queries where the worker_time is high. Similarly, if you were having an issue with IO, you could only capture SQL queries where the reads or writes are high. Note: The ORDER BY clause is only needed if you keep the TOP parameter in your query. For reference I've included below an example of the output of this query.

Poor SQL Query Output
Poor SQL Query Output
Poor SQL Query Output
Poor SQL Query Output

Also, if you click on the data in the "Plan" column it will display the execution plan in graphical format in a new tab.

Poor SQL Query Plan

Now that we have a query to capture what we are looking for, we need somewhere to store the data. The following table definition can be used to store the output of the above query. We just have to add this line ahead of the query above to take care of inserting the result, "INSERT INTO [DBA].[dbo].[My_Poor_Query_Cache]".

CREATE TABLE [DBA].[dbo].[My_Poor_Query_Cache] (
 [Collection Date] [datetime] NOT NULL,
 [Execution Count] [bigint] NULL,
 [Query Text] [nvarchar](max) NULL,
 [DB Name] [sysname] NULL,
 [Total CPU Time] [bigint],
 [Avg CPU Time (ms)] [bigint] NULL,
 [Total Physical Reads] [bigint] NULL,
 [Avg Physical Reads] [bigint] NULL,
 [Total Logical Reads] [bigint] NULL,
 [Avg Logical Reads] [bigint] NULL,
 [Total Logical Writes] [bigint] NULL,
 [Avg Logical Writes] [bigint] NULL,
 [Total Duration] [bigint] NULL,
 [Avg Duration (ms)] [bigint] NULL,
 [Plan] [xml] NULL
) ON [PRIMARY]
GO

Finally, we'll use the SQL Server Agent to schedule this query to run. Your application and environment will determine how often you want to run this query. If queries stay in your SQL cache for a long period of time then this can be run fairly infrequently, however if the opposite is true they you may want to run this a little more often so any really poor SQL queries are not missed. Here are a few snapshots of the job I created. The T-SQL to create this job can be found here.

Poor SQL Job Step
Poor SQL Job Schedule

That's it. Now, whenever you have spare time you can query this table and start tuning.

Next Steps



Related Tips: More | Become a paid author


Last Update: 2/7/2012

Share: Share 






Comments and Feedback:

Tuesday, February 07, 2012 - 9:02:52 AM - Jimbo Read The Tip

When I click on the Plan text, I get formatted XML, not an execution plan.


Tuesday, February 07, 2012 - 11:31:40 AM - Ben Snaidero Read The Tip

What version of Management Studio are you using?  I am using 2008 and get the plan in graphical format.  Unfortunately I don't have any older versions to test on.


Tuesday, February 07, 2012 - 3:04:17 PM - Ben Snaidero Read The Tip

A colleague of mine verified for me that if you are using SQL 2005 Management Studio then you do in fact just get formatted XML for the plan.


Tuesday, February 07, 2012 - 7:24:56 PM - zaim raza Read The Tip

very good SQL script. the same sort of report is available in sql server performance dashboard reports. so is there any difference between this script and and that reports ?

 

thanks.


Wednesday, February 08, 2012 - 10:16:50 AM - Ben Snaidero Read The Tip

I have not used the performance dashboard reports so I can't say exactly what the difference is if any.  Mine is based on the DMVs as I am sure the dashboard report is as well so I'd guess there would be some overlap.  The main benefit to doing it with your own script is you can customize it as much as you want as well as save the information for later analysis.  A query may only be in the cache for a short period of time so automatically gathering and storing this information is very beneficial.


Wednesday, February 08, 2012 - 12:55:27 PM - SandraV Read The Tip

Jimbo:  See Aaron's post on this subject:  http://sqlblog.com/blogs/aaron_bertrand/archive/2011/10/28/getting-graphical-showplan-back-in-sql-server-2008-r2.aspx.  Unfortunately, I had the problem before 2008 R2 SP1 and after.


Sunday, February 19, 2012 - 1:19:00 PM - Bob Barrows Read The Tip

I've used these in the past to attempt to analyze the application performance on my server with less than satisfactory results.  The top 10 worst performers are invariably the data-load queries that run once or twice per day during off-hours. If there was a way to exclude these, I would be very happy.

Of course, I'm talking about the performance dashboard results. Perhaps I could modify your example to exclude the data-load queries. Time to start experimenting I guess.


Sunday, February 19, 2012 - 11:48:34 PM - Ben Snaidero Read The Tip

Bob,

That's the main reason I prefer doing things myself with TSQL, lets me customize things as much or as little as I want/need to.  For the example you mention above you could add a where clause in the query from my tip similar to:

WHERE SUBSTRING(qt.text,qs.statement_start_offset/2 +1,
                 (CASE WHEN qs.statement_end_offset = -1
                       THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
                       ELSE qs.statement_end_offset END -
                            qs.statement_start_offset
                 )/2)   NOT LIKE '%INSERT STATEMENT TO EXCLUDE%'

This would always exclude those statements that you know there is nothing you can do to improve.

Ben.


Monday, February 20, 2012 - 7:06:35 AM - Bob Barrows Read The Tip

Yes, that was my first thought. The problem is, there are so many variations of that "INSERT STATEMENT TO EXCLUDE" that tit would probably take 24 hours to run this query. Perhaps I can filter on the duration, including only the queries enwhose durations are less than those in my current top ten - yes, that appears to be the way to go. Just keep decreasing the maximum duration until I start seeing queries that I need to focus on.


Tuesday, February 21, 2012 - 11:33:52 AM - Ian Stirk Read The Tip

Hi,

Very nice article!

Readers can discover a lot more about improving SQL performance via DMVs in this recently published book "SQL Server DMVs in Action"www.manning.com/stirk. It contains more than 100 scripts to identify problems, and offers a wide range of solutions.

Chapters 1 and 3 can be downloaded for free. Chapter 1 includes scripts for:

A simple monitor

Finding your slowest queries

Find your missing indexes

Identifying what SQL is running now

Quickly find a cached plan

Thanks

Ian 



Post a Comment or Question

Keep it clean and stay on the subject or we may delete your comment.
Your email address is not published. Required fields are marked with an asterisk (*)

*Name   *Email   Notify for updates
Comments
*Enter Code refresh code


 

Sponsor Information
"SQL doctor is the best SQL product on the market, by far. All of Idera's tools are great, but this is the icing on the cake!"

SQL Monitor – For database professionals who need results on Day One. Try it online.

SQL Servers keeping you up at night? Contact the Edgewood SQL Server DBAs for a Health Check. Prices start at $995.

Find and Fix SQL issues with Foglight Performance Analysis. Get a free copy.

Join the over million SQL Server Professionals who get their issues resolved daily.

Demystify TempDB Performance and Manageability


Copyright (c) 2006-2012 Edgewood Solutions, LLC All rights reserved
privacy | disclaimer | copyright | advertise | about
authors | contribute | feedback | giveaways | user groups
Some names and products listed are the registered trademarks of their respective owners.


Edgewood Solutions LLC | MSSharePointTips.com | MSSQLTips.com