Unfortunately, none of the above answers is correct. Beware of any "locking" solution that starts BEGIN TRAN SELECT
. Yes, if the isolation level is SERIALIZABLE, SELECT
creates locks that prevent other processes from updating the selected data. But what if no data are selected? What's to lock?
IOW, BEGIN TRAN
sets up a race condition:
/* spid */
/* 1 */ SELECT ... -- returns no rows
/* 2 */ SELECT ... -- returns no rows
/* 1 */ INSERT ... -- whew!
/* 2 */ INSERT ... -- error
To read before writing (say, to present data to the user), there's the special timestamp data type. In your case, though, it's just an insert. Use an atomic transaction, i.e. a single statement:
insert into [ua_subset_composite] (column1, column2)
values ('sadsadsad', 1)
where not exists (
select 1 from ua_subset_composite
where column1 = 'sadsadsad'
)
The server guarantees the row either is or is not inserted. The locking is done for you, where it needs to be, for the shortest possible time, by people who know how. :-)
I don't want to add a unique constraint
Well, you probably should, you know. The above code will prevent trying to add a nonunique value, and avoid an error message. A unique constraint will prevent someone less careful from succeeding.