Вы не говорите, какая база данных используется, но большинство крупных БД в наши дни имеют способ выполнить обновление и вернуть данные из строки, которая была обновлена
Например, в оракуле:
update car
set in_use = 1
where in_use = 0 and id = (select min(id) from car where in_use = 0)
returning id into car_id_that_was_set_in_use
Параметр car_id_that_was_set_in_use будет содержать идентификатор автомобиля, который был забронирован
Как отдельная операция, которая устанавливает блокировки и не удерживает транзакцию открытой в течение длительного времени, она не должна вызывать разногласий
MySQL, кажется, является заметным исключением - я не нахожу никаких признаков того, что MySQL поддерживает что-то вроде UPDATE..RETURNING
, но есть и другие обходные пути, такие как innodb, поддерживающие SELECT..FOR UPDATE
, чтобы позволить вам заблокировать нужную запись обновить и взломать с участием переменных, которые могут выглядеть примерно так:
UPDATE car SET
in_use = 1, id = @affectedid := id
WHERE in_use = 0 AND id=(SELECT MIN(id) FROM car WHERE in_use = 0);
SELECT @affectedid;
Проверьте это, хотя; Я никогда не использовал это и адаптировал из SO ответа
В качестве альтернативы, вы можете закодировать свое приложение переднего плана, чтобы его обойти, хотя оно менее эффективно. Это псевдокод, потому что я не делаю ruby:
int rowsupdated = 0
int potentialId = -1
while(rowsupdated = 0 and potentialId is not null) {
potentialId = sql_scalar("SELECT MIN(id) FROM car WHERE in_use = 0")
rowsupdated = sql_nonquery("UPDATE car SET in_use = 1 WHERE in_use = 0 and id = " + potentialId)
}
if(potentialId is null)
//there was no car to book, we tried them all - potentialId would only be null if there were no more cars
else
//potentialId now contains the id of the car we booked
Цикл while будет продолжаться, пока он не закажет машину. Это наивно и неэффективно, но поднимает важный вопрос, применимый также к предыдущему запросу
Запрос на обновление должен ссылаться на то же значение in_use, которое мы все еще ожидаем
Вы не можете выбрать идентификатор, просто идите вперед и установите in_use = 1, не принимая во внимание, установил ли кто-то еще in_use = 1, пока мы находились в режиме ожидания. Это называется оптимистичным параллелизмом - вы НАДЕЖДА, что никто не изменил данные в строке, которую вы хотите редактировать, но вы включили все данные, которые вы знаете о строке, так что если кто-то другой DID изменил строку, обновление завершится неудачно и вернет 0 Записи обновлены. Обновление завершится неудачно, если кто-то еще установит in_use = 1, пока мы бездействуем, и мы ставим условием обновления то, что in_use по-прежнему будет 0 для успешного обновления.
Если обновление возвращает 0, мы можем предположить, что кто-то еще изменил строку раньше, чем мы. Затем, зная, что мы не получили этот ряд, мы пытаемся найти другой (или принимаем решение перезаписать / объединить / принять изменения другого человека)