Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Data Management

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

Authors

Search

XML Feeds

Google Ads

« SQL Server sprawl, heard it twice in one dayInstance design; Where to do your work as a DBA and DB Developer »
comments
Rate Post:
submit to reddit Digg!FacebookDotnetkicks

This was asked on twitter the other day and I emailed the person the solution to this. The solution uses dynamic management views and it is not perfect because of a couple of reasons.
1) The dynamic management views don't keep this information forever, restart the server and your data is gone
2) If your table name is in a comment it will be picked up by this query
3) If the table name is part of another object it will also be picked up, for example if you have a table name customer and a view name customers then it will return a row if customers was part of the query but you are lloking for the customer table

So let's look at some code
First create the following stored procedure

  1. CREATE PROC prTestProc
  2. AS
  3. SELECT * FROM master..spt_values WHERE type = 'P'
  4. go

Now run this query 5 times

  1. SELECT * FROM master..spt_values WHERE type = 'P'

Run this query 6 times

  1. SELECT COUNT(*) FROM master..spt_values WHERE type = 'P'

Run this query 7 times

  1. SELECT COUNT(*) FROM master..spt_values

Run this query 8 times

  1. SELECT COUNT(*) FROM master..spt_values WHERE type <> 'P'

Run this stored procedure 9 times

  1. EXEC prTestProc

Now let's look at the output. Here is the query that returns all the queries, their execution counts, if they were ad hoc or not and their last execution time. The query works by using the sys.dm_exec_query_stats and sys.dm_exec_sql_text dynamic management views to bring back the SQL statements themselves.

  1. SELECT * FROM(SELECT COALESCE(OBJECT_NAME(s2.objectid),'Ad-Hoc') AS ProcName,execution_count,
  2.     (SELECT TOP 1 SUBSTRING(s2.TEXT,statement_start_offset / 2+1 ,
  3.       ( (CASE WHEN statement_end_offset = -1
  4.          THEN (LEN(CONVERT(NVARCHAR(MAX),s2.TEXT)) * 2)
  5.          ELSE statement_end_offset END)  - statement_start_offset) / 2+1))  AS sql_statement,
  6.        last_execution_time
  7. FROM sys.dm_exec_query_stats AS s1
  8. CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS s2 ) x
  9. WHERE sql_statement like '%spt_values%'
  10. AND sql_statement NOT like 'SELECT * FROM(SELECT coalesce(object_name(s2.objectid)%'
  11. ORDER BY execution_count DESC

Here is the output

ProcName	execution_count	sql_statement							last_execution_time
prTestProc	9		select * from master..spt_values where type = 'P'  		2009-08-03 10:11:38.810
Ad-Hoc		8		SELECT COUNT(*) FROM [master]..[spt_values] WHERE [type]<>@1	2009-08-03 10:11:22.857
Ad-Hoc		7		select count(*) from master..spt_values   			2009-08-03 10:11:19.107
Ad-Hoc		6		select count(*) from master..spt_values where type = 'P'  	2009-08-03 10:11:15.760
Ad-Hoc		5		select * from master..spt_values where type = 'P'  		2009-08-03 10:11:12.280

Let's look at the query in more detail

This line below has the name of the table we are searching for

  1. WHERE sql_statement like '%spt_values%'

The line below excludes the query that we are running itself since that is not what we want to return

  1. AND sql_statement NOT like 'SELECT * FROM(SELECT COALESCE(OBJECT_NAME(s2.objectid)%'

The line below will return Ad Hoc or the name of the object that the code was in, if s2.objectid is NULL then it was an Ad-Hoc query

  1. COALESCE(OBJECT_NAME(s2.objectid),'Ad Hoc')

As you can see this is probably good enough to give you some quick results to find out if a table is used so that you can drop it. The way I do this is I rename the table by prefixing it with 2 underscores, this enables two things for me
1) I can quickly find the table since it will be on top in the object explorer
2) I don't have to think what the original name was, I just remove the 2 leading underscores

Of course you can also run a trace and then store that in a file, this enables you then to parse the file with a trace tool (more on that later this week when I will blog about a couple of free trace tools, Ami Levin just notified me about a new free trace tool so I will include that one). You could also use Extended Events to do something like this but I like this approach because it is very simple and you can all do it in T-SQL




*** If you have a SQL related question try our Microsoft SQL Server Programming forum or our Microsoft SQL Server Admin forum

About the Author

User bio imageDenis has been working with SQL Server since version 6.5. Although he worked as an ASP/JSP/ColdFusion developer before the dot com bust, he has been working exclusively as a database developer/architect since 2002. In addition to English, Denis is also fluent in Croatian and Dutch, but he can curse in many other languages and dialects (just ask the SQL optimizer) He lives in Princeton, NJ with his wife and three kids.
Social SitingsTwitterFacebookLinkedInHomePageLTD RSS Feed
2639 views
submit to reddit Digg!FacebookDotnetkicks

Comments and Feedback

6 comments

Comment from: Michelle Ufford [Visitor] · http://sqlfool.com
*****
Thanks, Denis! This query was a big help for me. :)
08/03/09 @ 08:22
Comment from: Aaron Bertrand [Visitor] · http://sqlblog.com/blogs/aaron_bertrand/
*****
Careful about the LIKE check, on a case-sensitive collation it shouldn't filter out your stats query because the case does not match.
08/03/09 @ 09:16
Comment from: SQLDenis [Member] Email
Thanks Aaron, I guess I should have made LIKE check consistent to match at least the case of the query


I could have also done this of course

AND sql_statement NOT like 'SELECT * FROM(SELECT COALESCE(OBJECT_NAME(s2.objectid)%' COLLATE SQL_Latin1_General_CP1_CI_AS
08/03/09 @ 09:37
Comment from: onpnt [Member] Email
*****
Good stuff Denis
08/03/09 @ 11:04
Comment from: Kiran [Visitor] · http://www.technologyevaluation.com
****-
Another thing to watch out for - the LIKE clause you're using has an underscore in it ("WHERE sql_statement like '%spt_values%'")

T-SQL interprets the underscore as basically meaning "wildcard for one character". So, the search would find any string that had the words "spt", followed by any character, followed by "values". EG:

IF 'ABCdEFG' LIKE '%c_e%'
PRINT 'YES'
ELSE
PRINT 'NO'

will print "YES", because in the string 'ABCdEFG', "c" + any character + "e" does appear.

So, what you actually want in there, is this:

"WHERE sql_statement like '%spt[_]values%'"


Found your article very interesting though. I even adapted it as a stored proc, and set up another stored proc that would loop through all my tables in my database, and see which ones were seldomly used, so I could then investigate whether I wanted to delete those tables or not.
08/10/09 @ 07:00
Comment from: SQLDenis [Member] Email
Kiran ,

you are absolutely right about the underscore
08/10/09 @ 07:03

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)