I've been reading about possible solutions on upsert. In particular, this thread here, where the solution for Upsert went along the lines of:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
INSERT dbo.table(PK, ...)
END
COMMIT TRANSACTION;
However, with ISOLATION LEVEL SERIALIZABLE, I'll be introducing a lock at the table level, correct? Which is not ideal, as there will potentially be a lot of operations over that table using the Upsert.
I think we introduce the ISOLATION LEVEL SERIALIZABLE in the first place to prevent the issue where two concurrent transactions would be trying to update the same record non yet existent record and one of them would then fail INSERTing it. However, I was thinking of doing the following to solve that problem without the lock:
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
BEGIN TRY
INSERT dbo.table(PK, ...)
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
BEGIN
UPDATE dbo.table SET ... WHERE PK = @PK;
END
END CATCH
END
COMMIT TRANSACTION;
In this case, if the INSERT would fail, it would mean that in the mean time some other concurrent transaction inserted a row. Then, we would do the Update again.
However, I'm concerned if ERROR Handling is actually more expensive than using SERIALIZABLE. What do you guys think?
Thanks!