Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You also need <code>HOLDLOCK</code>.</p> <p>If the row does exist then your <code>SELECT</code> statement will take out a <code>U</code> lock on at least that row and retain it until the end of the transaction.</p> <p>If the row doesn't exist there is no row to take and hold a <code>U</code> lock in so you aren't locking anything. <code>HOLDLOCK</code> will lock at least the range where the row would fit in.</p> <p>Without <code>HOLDLOCK</code> two concurrent transactions can both do the <code>SELECT</code> for a non existent row. Retain no conflicting locks and both move onto the <code>INSERT</code>.</p> <p>Regarding the repro in your question it seems the "row doesn't exist" issue is a bit more complex than I first thought. </p> <p>If the row previously did exist but has since been logically deleted but still physically exists on the page as a "ghost" record then the <code>U</code> lock can still be taken out on the ghost explaining the blocking that you are seeing.</p> <p>You can use <code>DBCC PAGE</code> to see ghost records as in this slight amend to your code.</p> <pre><code>SET NOCOUNT ON; DROP TABLE ImportSourceMetadata CREATE TABLE ImportSourceMetadata ( SourceId NVARCHAR(50), LastCheckpoint DATETIME, PRIMARY KEY(SourceId) ) BEGIN TRAN SET TRANSACTION ISOLATION LEVEL READ COMMITTED SELECT LastCheckpoint FROM ImportSourceMetadata WITH (UPDLOCK) WHERE SourceId = 'Source' INSERT INTO ImportSourceMetadata VALUES ('Source', '2013-12-21') DECLARE @DBCCPAGE NVARCHAR(100) SELECT TOP 1 @DBCCPAGE = 'DBCC PAGE(0,' + CAST(file_id AS VARCHAR) + ',' + CAST(page_id AS VARCHAR) + ',3) WITH NO_INFOMSGS' FROM ImportSourceMetadata CROSS APPLY sys.fn_physloccracker(%%physloc%%) ROLLBACK TRAN DBCC TRACEON(3604) EXEC (@DBCCPAGE) DBCC TRACEOFF(3604) </code></pre> <p>The SSMS messages tab shows</p> <pre><code>Slot 0 Offset 0x60 Length 31 Record Type = GHOST_DATA_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS Record Size = 31 Memory Dump @0x000000001215A060 0000000000000000: 3c000c00 00000000 9ba20000 02000001 †&lt;.......¢...... 0000000000000010: 001f0053 006f0075 00720063 006500††††...S.o.u.r.c.e. Slot 0 Column 1 Offset 0x13 Length 12 Length (physical) 12 </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload