I have a table called SerialNumber
in my database that looks like this:
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[WorkOrderId] [int] NOT NULL,
[SerialValue] [bigint] NOT NULL,
[SerialStatusId] [int] NOT NULL
Where in my application I always need to get the MIN
SerialValue
with a SerialStatusId
of 1 for a given Work Order (WorkOrderId
).
i.e if a client have selected a certain serial number it should immediately change to SerialStatusId =2
so that the next client will not select the same value.
The application is installed on multiple stations which may access the Database simultaneously.
I tried a few approaches to solve this but without success:
update SerialNumber
set SerialStatusId = 2
output inserted.SerialValue
where WorkOrderId = @wid AND
SerialValue = (select MIN(SerialValue) FROM SerialNumber where SerialStatusId = 1)
AND
declare @val bigint;
set @val = (select MIN(SerialNumber.SerialValue) from SerialNumber where SerialStatusId = 1
and WorkOrderId = @wid);
select SerialNumber.SerialValue from SerialNumber where SerialValue = @val;
update SerialNumber set SerialStatusId = 2 where SerialValue = @val;
And the way I tested to see that don't select identical values is:
public void Test_GetNext()
{
Task t1 = Task.Factory.StartNew(() => { getSerial(); });
Task t2 = Task.Factory.StartNew(() => { getSerial(); });
Task t3 = Task.Factory.StartNew(() => { getSerial(); });
Task t4 = Task.Factory.StartNew(() => { getSerial(); });
Task t5 = Task.Factory.StartNew(() => { getSerial(); });
Task.WaitAll(t1, t2, t3, t4, t5);
var duplicateKeys = serials.GroupBy(x => x)
.Where(group => group.Count() > 1)
.Select(group => group.Key).ToList();
}
private List<long> serials = new List<long>();
private void getSerial()
{
object locker = new object();
SerialNumberRepository repo = new SerialNumberRepository();
while (serials.Count < 200)
{
lock (locker)
{
serials.Add(repo.GetNextSerialWithStatusNone(workOrderId));
}
}
}
All the tests I've made so far gave ~70 duplicate values from a table that contains 200 serials.
How can I improve my SQL statement so it won't return duplicate values?
UPDATE
Tried to implement what @Chris suggeted but still getting the amount of duplicated:
SET XACT_ABORT, NOCOUNT ON
DECLARE @starttrancount int;
BEGIN TRY
SELECT @starttrancount = @@TRANCOUNT
IF @starttrancount = 0 BEGIN TRANSACTION
update SerialNumber
set SerialStatusId = 2
output deleted.SerialValue
where WorkOrderId = 35 AND
SerialValue = (select MIN(SerialValue) FROM SerialNumber where SerialStatusId = 1)
IF @starttrancount = 0
COMMIT TRANSACTION
ELSE
EXEC sp_releaseapplock 'get_code';
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND @starttrancount = 0
ROLLBACK TRANSACTION
RAISERROR (50000,-1,-1, 'mysp_CreateCustomer')
END CATCH