Может ли массовое обновление уникального столбца завершиться ошибкой, даже если итоговый набор значений не содержит дубликатов?
Да. При обычной немедленной проверке уникальности СУБД может проверять ограничение после изменения каждой строки, поэтому промежуточный дубликат вызывает ошибку, даже если после обновления всех строк значения стали бы уникальными. Для перестановки уникальных значений нужны отложенная проверка ограничения или безопасный промежуточный этап.
Уникальное ограничение предназначено для защиты инварианта: одновременно в таблице не должно существовать двух строк с одинаковым ключевым значением. На практике DML-операции часто реализуются как последовательная обработка затронутых строк, поэтому СУБД должна контролировать нарушение ограничения не только по предполагаемому финальному результату, но и в процессе выполнения.
Предположим, две строки должны обменяться значениями уникального столбца. Если первая строка получает значение второй, это значение ещё занято второй строкой. Немедленная проверка воспринимает ситуацию как нарушение уникальности и отклоняет операцию.
Нельзя полагаться на порядок обработки строк: он может зависеть от плана выполнения, индекса, блокировок и особенностей конкретной СУБД. Поэтому корректный итоговый набор значений сам по себе не гарантирует успешное выполнение обновления.
Уникальность может проверяться немедленно, после изменения строки, либо отложенно, в конце оператора или транзакции — если конкретная СУБД и ограничение это поддерживают. При отложенной проверке временный дубликат допустим, если к моменту проверки финальное состояние соответствует ограничению.
В PostgreSQL перестановку можно выразить так:
Здесь ограничение проверяется при завершении транзакции, когда значения уже поменялись местами. Если отложенная проверка недоступна, применяют временные значения, гарантированно свободные для уникального столбца, а затем выполняют второе обновление. Такой способ требует аккуратно выбрать временный диапазон и учитывать блокировки.
Важно отличать уникальность итогового состояния от момента проверки ограничения. Поведение, поддержка отложенных ограничений и детализация счётчика изменённых строк зависят от СУБД, поэтому переносимый код не должен рассчитывать на конкретный порядок обработки строк.
В таблице товаров поле позиции уникально в пределах каталога, и менеджер меняет местами товары с позициями 10 и 20. Прямое обновление может столкнуться с занятым значением уже при обработке первой строки.
Вариант с двумя обновлениями через временные позиции работает почти в любой СУБД, но увеличивает сложность и требует транзакции, чтобы промежуточное состояние не стало видимым другим операциям. Отложенное уникальное ограничение проще концептуально и сохраняет одну логическую операцию, но поддерживается не всеми СУБД и может дольше удерживать блокировки.
Если система поддерживает отложенную проверку, выбран этот вариант: он явно описывает необходимость временного нарушения инварианта внутри транзакции и оставляет проверку финального состояния СУБД. В противном случае используется временный диапазон с обязательной транзакцией и проверкой конфликтов.
Достаточно ли одной транзакции без отложенного ограничения?
Нет. Транзакция гарантирует атомарность и изоляцию, но не обязана откладывать проверку уникальности до конца транзакции. Если ограничение немедленное, ошибка может возникнуть внутри оператора, после чего транзакция станет прерванной или будет откатить этот оператор — в зависимости от СУБД.
Можно ли решить проблему, обновляя строки в правильном порядке?
Иногда конкретный порядок действительно предотвращает конфликт, но полагаться на него нельзя. План выполнения может измениться, а SQL обычно не обещает порядок обработки строк без специальных гарантий. Надёжнее использовать отложенное ограничение или явно заданные временные значения.
Почему временное значение должно быть защищено транзакцией?
Без транзакции другие сеансы могут увидеть промежуточное состояние или занять временное значение. Это создаёт гонки, кратковременную неконсистентность и новые нарушения уникальности. Транзакция делает последовательность промежуточного и финального обновлений одной атомарной операцией для внешних наблюдателей.