victor_snezhko (victor_snezhko) wrote,
victor_snezhko
victor_snezhko

  • Mood:

Про добавление записей в таблицы с автоинкрементными полями в случаях, ...

... когда нужно знать значение этих автоинкрементных полей.

Вот блин, livejournal не разрешает делать темы сообщения длиннее ста символов...

Но речь не об этом.

Проблема: есть две таблички, в одной автоинкрементный первичный ключ, в другой есть внешний ключ, ссылающийся на первый. Нужно добавить строчки в обе таблицы таким образом, чтобы вторая строчка с помощью внешнего ключа ссылалась на первую.

OLE DB провайдер от Microsoft для их SQL-сервера вместе с библиотекой ADO предлагают простое, очевидное и неправильное решение. После выполнения функции ADO Update для записи изменений в базу колонки, соответствующие автоинкрементным полям, автоматически заполняются значениями, которые получились после инкремента. Обычно значения совпадают с теми, которые присвоились колонкам, но этого никто нигде не гарантирует. Более того. В профилере видно, что для заполнения этих колонок провайдер использует тупую функцию @@IDENTITY, которая возвращает значение последней автоувеличившейся identity-колонки. При нестрогой изоляции транзакций это может привести к тому, что значение колонки заполнится из совсем другого инкрементного поля. В базе получится каша.

Для решения проблемы сегодня пришла в голову простая мысль, которая настолько очевидна, что даже странно, почему я раньше до неё не додумался. Автоинкрементное поле надо заполнять руками. В postgresql даже можно узнать, какое следующее значение выдаст последовательность - объект, отвечающий за автоинкременты. Вот оттуда и надо брать значение и пытаться записать его в базу. Если не получилось - это означает, что вмешалась другая транзакция, и надо повторить - по-новой взять значение и по-новой попытаться записать. И так некоторое разумное количество раз. Остаётся только не забыть поправить sequence за собой. Тут то же самое. При обновлении может вмешаться другая транзакция. Это решается просто - при определении, какое значение задать автоинкрементному полю, надо не только узнать значение в sequence, но и проверить максимальное значение автоинкрементного поля в базе. через некоторое количество итераций устаканится. Как только хоть одна запись прошла успешно - всё, мы можем использовать этот ID. Совсем в идеале можно сделать случайные таймауты из диапазона, увеличивающегося с каждой коллизией, как в Ethernet.
Tags: базы данных
  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 1 comment