я таблицу с именем SerialNumber
в моей базе данных, которая выглядит следующим образом:Предотвращение одновременного выбора же следующего расчетного значения
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[WorkOrderId] [int] NOT NULL,
[SerialValue] [bigint] NOT NULL,
[SerialStatusId] [int] NOT NULL
Где в моем приложении я всегда нужно получить MIN
SerialValue
с SerialStatusId
1 для данный Рабочий заказ (WorkOrderId
).
Если клиент выбрал определенный серийный номер, он должен немедленно изменить на SerialStatusId =2
, чтобы следующий клиент не выбрал одно и то же значение.
Приложение установлено на нескольких станциях, которые могут одновременно обращаться к базе данных.
Я попробовал несколько подходов для решения этой проблемы, но без успеха:
update SerialNumber
set SerialStatusId = 2
output inserted.SerialValue
where WorkOrderId = @wid AND
SerialValue = (select MIN(SerialValue) FROM SerialNumber where SerialStatusId = 1)
И
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;
И так я проверил, чтобы убедиться, что не выбирать одинаковые значения является:
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));
}
}
}
Все тесты, которые я сделал до сих пор, дали ~ 70 дубликатов значений из таблицы, содержащей 200 серий. Как я могу улучшить свою инструкцию SQL, чтобы она не возвращала повторяющиеся значения?
UPDATE
Пытался осуществить то, что suggeted но @ Крис все еще получает количество дублируется:
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
Re: update - связанный 'sp_getapplock' будет необходим до утверждения update. Обратите внимание, что это эффективно сериализует вызовы этого кода - я считаю, что вам не нужно быть столь же пессимистичным, как это, учитывая, что теоретически множественные параллельные вызовы должны быть доступны для разных '[WorkOrderId]'. Кроме того, я сделал опечатку в своем предыдущем ответе w.r.t. – StuartLC