简体   繁体   中英

SQL TRANSACTION Left join result in null

I have a problem where I insert User and Address in a transaction with a 10 second delay and if run my select statement during the execution of the transaction it will wait for transaction to finish but I will get a null on the join. Why don't my select wait for both User/Address data to be committed. If I run the select statement after the transaction is finish I will get the correct result. Why do i get this error and what is the generic solution to make this work

BEGIN TRANSACTION   
 insert into user(dummy) values('text')
 WAITFOR DELAY '00:00:10';
 insert into address(ID_FK) values((SELECT SCOPE_IDENTITY()))
COMMIT TRANSACTION

Running during transaction result in null in join

select * from user u left join address a on u.id = a.ID_FK order by id desc
| ID | dummy | ID_FK | 
| 101 | 'text' | null |

Running after transaction result in correct result

select * from user u left join address a on u.id = a.ID_FK order by id desc
| ID | dummy |  ID_FK|
| 101 | 'text' | 101 |

在此处输入图像描述

This type of thing is entirely possible at default read committed level for on premise SQL Server as that uses read committed locking. It is then execution plan dependent what will happen.

An example is below

CREATE TABLE [user]
  (
     id    INT IDENTITY PRIMARY KEY,
     dummy VARCHAR(10)
  );

CREATE TABLE [address]
  (
     ID_FK INT REFERENCES [user](id),
     addr  VARCHAR(30)
  ); 

Connection One

BEGIN TRANSACTION

INSERT INTO [user]
            (dummy)
VALUES     ('text')

WAITFOR DELAY '00:00:20';

INSERT INTO address
            (ID_FK,
             addr)
VALUES     (SCOPE_IDENTITY(),
            'Address Line 1')

COMMIT TRANSACTION

Connection Two (run this whilst connection one is waiting the 20 seconds)

SELECT *
FROM   [user] u
       LEFT JOIN [address] a
              ON u.id = a.ID_FK
ORDER  BY id DESC
OPTION (MERGE JOIN) 

Returns

id dummy ID_FK addr
1 text NULL NULL

The execution plan is as follows

在此处输入图像描述

The scan on User is blocked by the open transaction in Connection 1 that has inserted the row there. This has to wait until that transaction commits and then eventually gets to read the newly inserted row.

Meanwhile the Sort operator has already requested the rows from address by this point as it consumes all its rows in its Open method (ie during operator initialisation). This is not blocked as no row has been inserted to address yet. It reads 0 rows from address which explains the final result.

If you switch to using read committed snapshot rather than read committed locking you won't get this issue as it will only read the committed state at the start of the statement so it isn't possible to get this kind of anomaly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM