(Edited 2/27/10 to add database name to the output)
Here's a little script I knocked up this afternoon to tell me who has open transactions on the server – not just the single oldest active transaction that DBCC OPENTRAN returns.
It gives back:
-
session ID
-
login name
-
database context
-
transaction begin time
-
how many log records have been generated by the transaction
-
how much log space has been taken up by those log records
-
how much log space has been reserved in case the transaction rolls back
-
the last T-SQL that was executed in the context of the transaction
-
the last query plan that was executed (only for currently executing plans)
It's ordered by the transaction begin time. I had some trouble using CROSS APPLY with the sys.dm_exec_query_plan DMV – if the plan isn't available, it blows out the entire result-set for that transaction. After messing around for ten minutes I discovered the joys of the OUTER APPLY operator – which is the same as CROSS APPLY but allows NULL values from the function being cross-applied.
Also thanks to fellow MVP Aaron Bertrand (twitter|blog) for pointing out a mistake in the way I was calling sys.dm_exec_query_plan.
Here's the script with some example output:
SELECT s_tst.[session_id],
s_es.[login_name] AS [Login Name],
DB_NAME (s_tdt.database_id) AS [Database],
s_tdt.[database_transaction_begin_time] AS [Begin Time],
s_tdt.[database_transaction_log_record_count] AS [Log Records],
s_tdt.[database_transaction_log_bytes_used] AS [Log Bytes],
s_tdt.[database_transaction_log_bytes_reserved] AS [Log Rsvd],
s_est. AS [Last T-SQL Text],
s_eqp.[query_plan] AS [Last Plan]
FROM sys.dm_tran_database_transactions s_tdt
JOIN sys.dm_tran_session_transactions s_tst
ON s_tst.[transaction_id] = s_tdt.[transaction_id]
JOIN sys.[dm_exec_sessions] s_es
ON s_es.[session_id] = s_tst.[session_id]
JOIN sys.dm_exec_connections s_ec
ON s_ec.[session_id] = s_tst.[session_id]
LEFT OUTER JOIN sys.dm_exec_requests s_er
ON s_er.[session_id] = s_tst.[session_id]
CROSS APPLY sys.dm_exec_sql_text (s_ec.[most_recent_sql_handle]) AS s_est
OUTER APPLY sys.dm_exec_query_plan (s_er.[plan_handle]) AS s_eqp
ORDER BY [Begin Time] ASC;
GOsession_id Login Name Database Begin Time Log Records Log Bytes Log Rsvd Last T-SQL Text Last Plan
———- —————– ——– ———————– ———– ——— ——– ———————————— ———
54 ROADRUNNERPR\paul foo 2010-02-01 15:28:48.560 2 236 8550 begin tran insert into t1 values (1) NULL
55 ROADRUNNERPR\paul foo 2010-02-01 16:38:18.373 3 356 8852 insert into t1 values (3) NULL
14 Responses to Script: open transactions with text and plans
Very useful, thank you for sharing! Though I’m very surprised at the value for Log Bytes Reserved – it’s a lot higher than I thought it would be.
Great script Paul. I added a column for database, since I would run this kind of thing on a system with 100s of databases.
Actually now that I ran it on several systems and did not observe a single query plan came back, I looked closer and suspect that you need to pass a plan_handle into sys.dm_exec_query_plan, not a sql_handle. So I changed the OUTER APPLY to this:
LEFT OUTER JOIN sys.dm_exec_requests AS s_req
ON s_ec.[session_id] = s_req.[session_id]
OUTER APPLY sys.dm_exec_query_plan (s_req.plan_handle) AS s_eqp
This means you’ll only get the plan for requests that are currently active, of course. And this makes the query take a little longer. :-)
Thanks Aaron – you’re right. I’ve updated the script.
Hi Paul,
Have you had a look at my Who is Active script? It does all of this and much, much more. To get the log record counts use the @get_transaction_info = 1 mode.
http://sqlblog.com/blogs/adam_machanic/archive/2009/12/03/who-is-active-v9-57-fast-comprehensive-dmv-collection.aspx
Great script, Paul, thanks. I’ve noticed on our system we often get a number of entries where the Begin Time is NULL, and all the log columns are zero. What does that mean?
Thanks.
Nice Script. I have been using a script very similar to this one. It works beautifully.
Nice script. You’re immortalized (by name) in my code library.
Very nice script Paul, it works fine!
@Adam Nope not yet – always been on my list to try out.
@Jerry They’re read-only transactions that your code starts with an explicit BEGIN TRAN. SQL doesn’t know yet whether they’re read-only so has to count them in the active transaction list.
Hi Paul,
Very useful script – a big thanks
Great script Paul!
I ran a test and for me something unexpected happened. I had two windows one with begin tran and another with your script. However, begin tran did not register until some subsequent code was run in the window. I expected an open transaction after the begin tran but apparently not so. I would have thought the open tran would have written to the transaction log. As they say every days a school day.
No – simply doing a BEGIN TRAN doesn’t do anything. The transction isn’t ‘active’ and hasn’t taken any locks. As soon as you do a SELECT inside the transaction, it will show up. Nothing gets written to the transaction log unless the transaction changes the database in some way. I’ll do a blog post about this.
Cheers
Thanks Paul!
It makes sense. I am looking forward to your blog on this.
For me this is possibly one of those areas Adam Machinac was referring to when he blogged about puzzling things that don’t behave the way you think.