Как изменяется реквизит ВерсияДанных в 1С и как определить хронологию изменений объектов?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с объектными сущностями в платформе 1С (такими как справочники, документы, бизнес-процессы и др.) разработчики часто сталкиваются со служебным реквизитом ВерсияДанных. Этот реквизит выглядит как непонятная строка символов в кодировке Base64 (например, AAAAAB32JcE=). В процессе изменения и записи объекта эта строка меняется.

В этой статье мы подробно разберем, по какому алгоритму вычисляются значения версий объектов, почему они могут резко увеличиваться, как сравнить две версии для определения хронологии изменений и как программно преобразовать эту строку в привычное десятичное число на встроенном языке 1С.

Как реквизит ВерсияДанных хранится на уровне СУБД

Для того чтобы понять природу изменения версий, заглянем под капот платформы 1С и посмотрим, как эти данные представлены в базе данных. Для каждой таблицы объектов СУБД платформа автоматически создает системное поле с именем _Version.

Особенности хранения зависят от используемой СУБД:

Таким образом, на уровне базы данных версия — это обычное 8-байтовое число, которое при передаче в код 1С преобразуется в строку формата Base64 для удобства работы и унификации.

Почему значения версий «скачут» при записи

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

Версия 1: AAAAAB32JcE=

Версия 2: AAAAAB32KUA=

Однако в реальной рабочей базе данных при последовательном изменении одного и того же элемента (например, номенклатуры) мы можем увидеть резкие скачки. Разница между значениями может составлять сотни и тысячи единиц. Разберем, почему это происходит.

Счетчик версий (rowversion в SQL Server) является глобальным для всей базы данных, а не локальным для конкретной строки или таблицы. Это означает следующее:

  1. Любая транзакция записи, создания или изменения строки в любой таблице базы данных, содержащей поле версии, увеличивает этот глобальный счетчик.
  2. Если в вашей базе данных активно работают пользователи, фоновые задания или выполняются обмены, то за доли секунды между первым и вторым сохранением вашего элемента номенклатуры счетчик успеет увеличиться на огромное число за счет записи других документов, справочников или движений регистров.
  3. Поэтому разница между версиями одного объекта почти никогда не будет равна единице в реальной многопользовательской среде.

Как определить, какой из объектов был записан раньше

Выясним, можно ли определить хронологическую последовательность изменений объекта по значению его версии. Да, это сделать очень просто.

Поскольку версия — это последовательно увеличивающееся число, приведенное к формату Base64, для определения хронологии достаточно сравнить строковые представления версий. В Base64 символы упорядочены по возрастанию их двоичного кода (где символ А эквивалентен нулю).

Правило простое: чем больше строка лексикографически (или при посимвольном сравнении), тем старше версия и тем позже был изменен объект. Сравнение строк работает абсолютно корректно и полностью отражает хронологию изменений на уровне СУБД.

Преобразование версии данных в число на встроенном языке 1С

Рассмотрим, как преобразовать строковое представление версии данных (Base64) в шестнадцатеричный вид, а затем получить из него привычное десятичное число.

Если мы попытаемся преобразовать двоичные данные версии в строку в 1С, платформа неявно представит их в виде шестнадцатеричных байтов, разделенных пробелами. Например:

Версия AAAAAB32PyY= преобразуется в 00 00 00 00 1D F6 3F 26

Версия AAAAAB32Q0Y= преобразуется в 00 00 00 00 1D F6 43 46

Для перевода этих значений в десятичную систему счисления мы можем написать простую и эффективную десериализацию прямо во встроенном языке 1С. Проанализируем пример кода, который решает эту задачу:


// Пример получения десятичного числа из Base64-строки версии данных
СтрокаBase64_1 = "AAAAAB32PyY=";
СтрокаBase64_2 = "AAAAAB32Q0Y=";

// Шаг 1. Получаем двоичные данные из Base64
ДвоичныеДанные_1 = Base64Значение(СтрокаBase64_1);
ДвоичныеДанные_2 = Base64Значение(СтрокаBase64_2);

// Шаг 2. Преобразуем двоичные данные в hex-строку без пробелов с префиксом 0x
// При приведении ДвоичныхДанных к строке 1С выдает шестнадцатеричные байты через пробел
СтрокаHex_1 = "0x" + СтрЗаменить(Строка(ДвоичныеДанные_1), " ", "");
СтрокаHex_2 = "0x" + СтрЗаменить(Строка(ДвоичныеДанные_2), " ", "");

// Шаг 3. Преобразуем hex-строку в десятичное число
ЧислоВерсии_1 = ЧислоИзШестнадцатеричнойСтроки(СтрокаHex_1);
ЧислоВерсии_2 = ЧислоИзШестнадцатеричнойСтроки(СтрокаHex_2);

Сообщить("Десятичное значение версии 1: " + Формат(ЧислоВерсии_1, "ЧГ=0")); // Результат: 502677286
Сообщить("Десятичное значение версии 2: " + Формат(ЧислоВерсии_2, "ЧГ=0")); // Результат: 502678342

Этот лаконичный код позволяет быстро и без использования внешних библиотек или онлайн-калькуляторов получить точные числовые значения версий непосредственно в коде вашей конфигурации.

Значение версии для новых (еще не записанных) объектов

Важно помнить, как ведет себя платформа 1С при работе с новыми, еще ни разу не записанными в базу данных объектами. В этот момент у них еще нет физической записи в СУБД, а значит, и счетчик версии для них не определен.

Основная задача реквизита: Оптимистическая блокировка

Понимание работы реквизита ВерсияДанных помогает осознать, как платформа 1С реализует механизм оптимистической блокировки. Этот механизм защищает данные от коллизий, когда два пользователя одновременно открывают и редактируют один и тот же элемент.

Разобьем этот процесс по шагам:

  1. Когда Пользователь А и Пользователь Б одновременно открывают форму одного справочника, платформа считывает данные из СУБД на клиентские машины. Вместе с реквизитами считывается и кэшируется текущая версия данных (например, AAAAAB32PyY=).
  2. Пользователь А вносит изменения быстрее и нажимает кнопку «Записать».
  3. Платформа отправляет в базу данных запрос обновления (UPDATE), проверяя в секции WHERE соответствие идентификатора объекта и его версии:
    WHERE _IDRRef = @Идентификатор AND _Version = @КэшированнаяВерсия Запись успешно проходит, а глобальный счетчик СУБД автоматически присваивает строке новую версию (например, AAAAAB32Q0Y=).
  4. Пользователь Б дописывает свои изменения и тоже нажимает «Записать». Платформа пытается выполнить аналогичный запрос UPDATE, где в секции WHERE по-прежнему указана старая версия AAAAAB32PyY=.
  5. СУБД возвращает результат: измененных строк — 0 (так как в базе уже записана версия AAAAAB32Q0Y=).
  6. Платформа 1С понимает, что данные были изменены другим процессом, транзакция откатывается, а пользователь Б видит системную ошибку: «Данные были изменены или удалены другим пользователем».

Особенности использования в запросах

Реквизит ВерсияДанных доступен разработчикам для чтения во встроенном языке и запросах. Однако, поскольку это системное и виртуальное поле, на него накладываются некоторые важные ограничения в языке запросов 1С:

Используя описанные методы работы с версиями данных, вы сможете легко отслеживать изменения объектов, строить отчеты по хронологии модификации записей и эффективно отлаживать механизмы синхронизации и оптимистических блокировок в ваших информационных базах 1С.

← На главную