Querying the sys.dm_tran_locks
DMV shows us which sessions (SPIDs) are holding locks on resources like table, page and row.
For each lock acquired, is there any way to determine which SQL statement (delete, insert, update or select) caused that lock?
I know that the most_recent_query_handle
column of the sys.dm_exec_connections
DMV gives us the text of the last query executed, but several times other queries ran before under the same session (SPID) and are still holding locks.
I already use the sp_whoisactive
procedure (from Adam Machanic) and it only shows the query that is on the input buffer at the moment (think DBCC INPUTBUFFER @spid
), which not always (and in my case usually never) is the query that acquired the lock.
For example:
- open transaction/session
- exec a statement (that holds a lock on a resource)
- exec another statement on the same session
- open another transaction/session and try to modify the resource locked at step 2.
The sp_whoisactive
procedure will point out the statement at step 3, which is not the responsible for the lock, and thus not useful.
This question came from doing an analysis using the Blocked Process Reports feature, to find the root cause of blocking scenarios in production. Each transaction runs several queries, and most of time the last one (that is shown on input buffer at BPR) is rarely the one holding the lock.
I have a follow-up question: Framework to effectively identify blocking queries
Best Answer
SQL Server doesn't keep a history of the commands that have been executed1,2. You can determine what objects have locks, but you cannot necessarily see what statement caused those locks.
For example, if you execute this statement:
And look at the SQL Text via the most recent sql handle, you'll see that statement does show up. However, if the session did this:
You'd only see the
SELECT * FROM dbo.TestLock;
statement, even though the transaction hasn't been committed, and theINSERT
statement is blocking readers against thedbo.TestLock
table.I use this for looking for uncommitted transactions that are blocking other sessions:
If we setup a simple test-bed in SSMS with a couple of query windows, we can see that we can only see the most recently active statement.
In the first query window, run this:
In the second window, run this:
Now, if we run the uncommitted blocking transactions query from above, we see the following output:
(I've removed some irrelevant columns from the end of the results).
Now, if we change the first query window to this:
and re-run the 2nd query window:
We'll see this output from the blocking transactions query:
1 - not entirely true. There is the procedure cache, which may contain the statement responsible for the lock. However, it may not be easy to determine which statement is the actual cause of the lock since there may be many queries in the cache that touch the resource in question.
The query below shows the query plan for the test queries above since my procedure cache is not very busy.
The results of this query may allow you to find the culprit, but be aware, inspecting the procedure cache like this can be quite demanding on a busy system.
2SQL Server 2016 and above offer the Query Store, which does retain complete history of queries executed.