Sql-server – UPDATE Blocking SELECT Of Unrelated Rows

sql server

I have TableA with Col1 as the primary key. I am running the following transaction without committing it (for test purposes).

BEGIN TRANSACTION
UPDATE TableA
SET Col3 = 0
WHERE Col2 = 'AAA'

In the meanwhile, I run the following query and see that it waits on the first transaction to complete.

SELECT *
FROM TableA
WHERE Col2 = 'BBB'

But the following query returns the results immediately:

SELECT *
FROM TableA
WHERE Col1 = '1'

So I thought that the second query might need to read rows that have exclusive locks put by the first transaction in order to select rows with Col2 = 'BBB'. That's why then I tried to index Col2 so that a table seek will not be necessary but that did not work either. Second query still waits on the first transaction.

What should be done to prevent SELECT from blocking (except the use of NOLOCK).

P.S: Transaction isolation level is "Read Committed".

Best Answer

I might have to modify this answer once you provide more information based on my comment.

For now follow this example. I am using SQL 2016, SP1.

--Creating table
    DROP TABLE IF EXISTS TableA
    CREATE TABLE [dbo].[TableA](
      [col1] [int] NULL,
      [col2] [nchar](10) NULL,
      [col3] [int] NULL
    ) ON [PRIMARY]

   GO

Inserting data:

INSERT INTO [dbo].[TableA]
           VALUES (10, 'AAA', 11),
                  (12, 'BBB', 13),
                  (14, 'CCC', 15)

Session ID 65 running this.

BEGIN TRANSACTION
UPDATE TableA
SET Col3 = 0
WHERE Col2 = N'AAA'

Session ID 66 running this:

SELECT *
FROM TableA
WHERE Col2 = N'BBB'

Looking at the locks held by both session. Session 65 is holding an exclusive lock on file id:1, page id:488 and slot id=0, shared lock is requested on the same resource by session 66 and is waiting.

enter image description here

Running the 2nd query and looking at the actual execution plan it is doing a table scan and because of your isolation level reading requires a shared lock to prevent any update or delete on the same row.

enter image description here

Creating an index on col2 does not change the behavior in my case because of number of rows.

CREATE NONCLUSTERED INDEX [NCI_TableA_col2] ON [dbo].[TableA]
(
    [col2] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = ON, 
DROP_EXISTING = OFF, 
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

GO

After I inserted 3000+ rows I can get a index seek and session 66 will not wait for the session 65 anymore.

enter image description here

Why did your 3rd query ran without waiting: I can answer that once you provide more information.