Logging Activity Using sp_WhoIsActive – Take 2

We’ve already covered how to log activity using sp_WhoIsActive, but I wanted to share how I’ve seen it done in the wild. The difference between the two methods is that this version logs everything to the same table and is for continuous activity logging (scheduled job), whereas the other version uses a new table each time it logs activity and is for when you are working on a problem.

WHAT IS IT?

If you don’t know about sp_WhoIsActive already, you’ve been living under a rock. It’s just the most awesome, free SQL Server tool there ever was. Adam Machanic, the author of sp_WhoIsActive, says it “is a comprehensive DMV-based monitoring script, designed to tell you at a glance what processes are active on your SQL Server and what they’re up to.”

I’m a fan of using free tools that can help me do my job more effectively. I’ve used sp_WhoIsActive even when I’ve also had paid tools monitoring production.

GET IT

You can download sp_WhoIsActive here.

SCHEDULE IT

Create a new job and plop the below code into the job step, modifying the first 3 variables as needed. The code will create the logging table if it doesn’t exist, the clustered index if it doesn’t exist, log current activity and purge older data based on the @retention variable.

How often should you collect activity? I think collecting sp_WhoIsActive data every 30-60 seconds is a good balance between logging enough activity to troubleshoot production problems and the storage needed to keep the data in a very busy environment.

You could also break the DELETE out into its own job so that it runs once a day. If you do this, loop through the DELETEs in batches rather than all at once.

ANALYZE IT

This is just a starter query. I generally am looking at a specific time period based on a complaint from a user that queries were running slow earlier in the day. I once used the WhoIsActive logging table to identify who was causing tempdb to grow absurdly big. By the time I got to it, tempdb was 500GB in size! I switched my query to include tempdb_allocations and tempdb_current plus ORDER BY tempdb_allocations DESC, and it was immediately clear a business user was querying ALL.THE.THINGS.

Previous Post
sp_BlitzFirst @SinceStartup = 1 Shows You Wait Stats Since, Uh, Startup
Next Post
Where Do Missing Index Requests Come From?

46 Comments. Leave new

  • I have my own version of this here

    http://thomaspullen.wordpress.com/2011/04/04/session-capture-system/

    that also includes pruning of old data, etc. Doens’t use sp_whoisactive but the same underlying DMVs

    Reply
  • Tara, I’d like a way to be able to gather a list of all usernames **and** applications that attach to a database over time. This data can be found for historic connections via something like:

    select * from
    ::fn_trace_gettable(‘D:\Apps\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\log_589.trc’,0) x
    INNER JOIN sys.trace_events e ON eventclass = trace_event_id

    ….however, the SPID column within this corresponds to the process id on the connecting client machine, so to find out the corresponding application, you need to reads the running application spid’s from the desktop client, which is obviously not a particularly easy thing to do.

    Are you aware of any existing implementations of this idea?

    Reply
    • The only way I’ve been able to figure this out is by requiring that all applications specify the “Application Name” parameter in the connection string. If they don’t specify it, you are going to see something generic, which is not helpful.

      The SPID column in a trace is not the process id on the client machine. It is the process id of the connection within SQL Server. But maybe I am not understanding what you mean.

      Reply
      • > The SPID column in a trace is not the process id on the client machine. It is the process id of the connection within SQL Server.

        That would certainly make more sense, but I swear when I checked to confirm what I thought it was, that it was indeed the client spid – will have to double check that I guess.

        Reply
  • Thanks for a great article, just one question
    Why is the first column a varchar(8000) and not a time?

    Reply
    • “Why is the first column a varchar(8000) and not a time?”

      Because it’s a formatted column designed for using SSMS as a GUI. (Why 8000? Probably an error on my part.)

      My recommendation for data capture is to use the @format_output = 0 switch, which outputs all of the numbers as actual numbers rather than text.

      Great job, Tara!

      Reply
    • Oh and why is it not the TIME data type? Well, two reasons: A) The proc in its current state is designed to be backward compatible to SQL Server 2005, and there was no TIME data type back then. And B) I don’t think I can fit the information into the TIME data type; what if a request has been running for over 24 hours?

      Reply
      • Do you have a copy of sp_whoisactive with all the the azure related stuff (msdb ect) commented out. The last one you posted (sp_whoisactive_v11_112.sql) still has it in there. I’m at a new job and really want to get it working in Azure. I am trying to install is on the USER database, not the master.

        Reply
  • Does anyone have a copy of sp_whoisactive with all the the azure related stuff (msdb ect) commented out. The last one Adam posted (sp_whoisactive_v11_112.sql) still has it in there. I’m at a new job and really want to get it working in Azure.

    Reply
  • From the result of sp_whoisactive, is there a way to convert the physical reads to MB?

    Reply
    • Sure, with your ol’ archenemy MATH, muhahaha. Pages are 8KB, so you can do some division to get the numbers you’re looking for.

      Reply
      • Thanks … but how many pages sql server reads in one physical read… I mean if the “sp_whoisactive” value of [physical_reads] is 12345 how many pages is that? so I will multiply it by number of reads then by 8kb … I think there is one parameter missing in this equation unless each read is equivalent to one page, and I don’t think so, ain’t?!

        Reply
  • There are a lot of &gt &lt in this, is this a browser thing? Which browser should I use, I’m using Chrome. thanks

    e.g. SET @schema = REPLACE(@schema, ‘<table_name>’, @destination_table);

    Reply
  • getting this error..
    Msg 102, Level 15, State 1, Line 1
    Incorrect syntax near ‘<'.

    Reply
  • […] approaches have their merits, and neither is that uncommon.  However, the latter is much more regularly blogged about, so I’m going to present the former, because it kinda helped remind me of a few […]

    Reply
  • while running it on SQL 2012 getting below error.

    Invalid object name ‘sys.dm_exec_query_statistics_xml’.

    Reply
  • It’s funny how many times I’ve set these captures up over the years, and how many times I have to figure it all out again. Thanks for keeping the blog and posts around Brent!

    Reply
  • Hi Tara, thanks for sharing this tool. I’m not able to get rid of the error ” Column name ‘collection_time’ does not exist in the target table or view. ” when the script run, using sp_whoisactive Version 11.32 SQL Server 2012. I was wondering if you know a fix to this issue. Thanks

    Reply
    • I have not seen that issue. I’ve got a client running version 11.32 and running the script in the blog post every minute. No issues with the job. Perhaps check that the table is getting created properly – sounds like you’re missing collection_time – though the script should work as is. Perhaps you changed the script a little? All that you need to change is the values for the variables – nothing else.

      Reply
  • Gladys Fernandes
    September 10, 2019 2:34 am

    how to track DML changes using sp_whoisactive?

    Reply
  • joris bernaerts
    October 22, 2019 6:25 am

    Hey it seems in the second query the columns in are referenced incorrectly. In the scripts start_time is incorrect since it should be starttime

    Reply
    • No, start_time is correct. I double checked it just now in the latest sp_WhoIsActive in case Adam changed the column name. It’s still start_time. Adam’s naming convention is lower case and underscores to separate words. I don’t see a single starttime in sp_WhoIsActive when I use CTRL+F.

      Reply
  • Hi,
    I am able to log the sp-names in a table using whoisactive successfully but not able to log parameter’s values with sp-routine names, is it possible in it ?

    Reply
  • philipoluwaleye@yahoo.ie
    August 4, 2020 5:05 am

    Each time I run this it comes with error in job log – Executed as user: NT SERVICE\SQLSERVERAGENT. Warning: Null value is eliminated by an aggregate or other SET operation. [SQLSTATE 01003] (Message 8153) Column name or number of supplied values does not match table definition. [SQLSTATE 21S01] (Error 213) Warning: The join order has been enforced because a local join hint is used. [SQLSTATE 01000] (Error 8625) Warning: The join order has been enforced because a local join hint is used. [SQLSTATE 01000] (Error 8625). The step failed.

    Can you please guide on the fololwing

    1. Structure of the Table – “WhoIsActive” with defined data types
    2. Can the name in “WhoIsacive” be reused in statement – line 21 – SET @schema = REPLACE(@schema, ”, @destination_table);
    3. If 2 is not possible, please give table structure for teh table to be created and used in line 21

    Thanks

    Philip

    Reply
    • Philip, I believe that error is coming from sp_WhoIsActive. Get a newer version of it if yours is out-dated. Otherwise, get a slightly older version if you are on the latest. For the structure of the job, just run the script and it’ll create the table. You can check the columns and data types yourself at that point. You don’t need to do anything with line 21 or any of the lines except lines 3 through 6 which you can change to whatever you want.

      Reply
  • Philip Oluwaleye
    August 4, 2020 1:00 pm

    Thanks Tara for your quick response. I do not have WhoISActive before now today is my first experience with it. I have downloaded the original script and deployed the SPROC on master Db as specified by Adam Machanic. But that was SPROC and not table but I want to customse the entire process into sql agent job and collect data every 30sec . So If I 3 -6 is the only change to make – my retention is 15 days in line 3 and line 4 is WhoIsActive and line 5 is master per sysname . I left line 21 as you advised but the job failed and says WhoISActive object does not exists … It seems I have to create the table its not created automatically as suggested .. ro what Im still dong wrong ?

    Reply
    • Lines 14-23 are what creates the table. See Adam’s documentation here for info on the @schema option to help you troubleshoot this: http://whoisactive.com/docs/25_capturing/. Last thing I’d suggest to try is to run lines 1-23 separately in a query window and see if the table gets created. If that doesn’t fix it, you’ll need to troubleshoot it further as it’s very hard to troubleshoot through blog post comments.

      Reply
  • Is it possible to sort currently active queries based on their high CPU usage, and monitor how their execution time fluctuates in minutes or seconds? As it stands, we can only see the currently active query, but we lack information about the duration of time a specific query occupies the CPU while running long. The frequency of the changes in CPU usage please?

    Reply
    • Teddy, buy a proper monitoring tool for that. We use SQL Sentry at Straight Path or whatever monitoring tool a client already has.

      Reply
  • Hello I am running v12 of sp_whoisactive on SQL 2022 CU6 which we just migrated to last weekend. Before this we where on SQL 2016 and the above job worked just fine. Now I am getting job failures for the whoisactive job everytime it is run through the agent. As soon as the job runs I get:
    Warning: The join order has been enforced because a local join hint is used.
    Warning: Null value is eliminated by an aggregate or other SET operation.
    Warning: The join order has been enforced because a local join hint is used.
    Warning: The join order has been enforced because a local join hint is used.
    Warning: Null value is eliminated by an aggregate or other SET operation.

    Which fails the job. I have tried downgrading to 11.35 and that works for the most part. But about every third or fourth run of the job I get the same issue which triggers a ticket in our ticketing system. For now I have had to abandon the job until I can come up with a fix. One thought I had was because its SQL 2022 and I dont see where the current stored proc says it works with this version. Any help would be appreciated.

    Reply
    • Have you tried dropping the WhoIsActive table (rename it if you care about the data) and letting the job recreate the table that it needs?

      Reply
    • Try updating your instance to the latest CU – I fixed this issue after updating my instance MSSQLS 2019 to CU24

      Reply
  • Gene B. Torres
    September 6, 2023 9:58 am

    When the server is under a little pressure, I get a lot of these:

    Invalid SPID 870 specified.

    Lock request time out period exceeded. –<— less frequent but sometimes

    XML datatype instance has too many levels of nested nodes. Maximum allowed depth is 128 levels. –<< and this one too but less frequent

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.