Возможно, хотя и громоздко, сделать это. Как говорит bortzmeyer , опасно полагаться на смежные значения последовательностей, поэтому лучше просто оставить все как есть.
Если вы не можете:
Каждый доступ к таблице, который может привести к тому, что строка будет иметь определенное имя (то есть, каждое INSERT
к этой таблице, и если вы разрешите это (хотя это плохая практика) каждые UPDATE
, что может изменить name
поле) должен сделать это внутри транзакции, которая сначала блокирует что-либо . Самый простой и наименее производительный вариант - просто заблокировать всю таблицу, используя LOCK users IN EXCLUSIVE MODE
(добавление последних 3 слов разрешает одновременный доступ для чтения другим процессам, что безопасно).
Однако это очень грубая блокировка, которая снижает производительность при наличии множества одновременных модификаций users
; лучшим вариантом будет блокировка одной соответствующей строки в другой таблице, которая уже должна существовать. Этот ряд можно заблокировать с помощью SELECT ... FOR UPDATE
. Это имеет смысл только при работе с «дочерней» таблицей, которая имеет зависимость FK от другой «родительской» таблицы.
Например, представьте себе на время, что мы на самом деле пытаемся безопасно создать новый orders
для customer
, и что эти ордера каким-то образом имеют идентифицирующие «имена». (Я знаю, плохой пример ...) orders
имеет зависимость FK от customers
. Затем, чтобы предотвратить создание двух заказов с одинаковым именем для данного клиента, вы можете сделать следующее:
BEGIN;
-- Customer 'jbloggs' must exist for this to work.
SELECT 1 FROM customers
WHERE id = 'jbloggs'
FOR UPDATE
-- Provided every attempt to create an order performs the above step first,
-- at this point, we will have exclusive access to all orders for jbloggs.
SELECT 1 FROM orders
WHERE id = 'jbloggs'
AND order_name = 'foo'
-- Determine if the preceding query returned a row or not.
-- If it did not:
INSERT orders (id, name) VALUES ('jbloggs', 'foo');
-- Regardless, end the transaction:
END;
Обратите внимание, что не достаточно просто заблокировать соответствующую строку в users
с помощью SELECT ... FOR UPDATE
- если строка еще не существует, несколько одновременных процессов могут одновременно сообщить, что строка не существуют, а затем пытаются выполнить одновременные вставки, что приводит к неудачным транзакциям и, таким образом, к пробелам в последовательности.
Любая схема блокировки будет работать; важно то, что любой, кто пытается создать строку с тем же именем, должен попытаться заблокировать один и тот же объект .