При работе с объектными сущностями в платформе 1С (такими как справочники, документы, бизнес-процессы и др.) разработчики часто сталкиваются со служебным реквизитом ВерсияДанных. Этот реквизит выглядит как непонятная строка символов в кодировке Base64 (например, AAAAAB32JcE=). В процессе изменения и записи объекта эта строка меняется.
В этой статье мы подробно разберем, по какому алгоритму вычисляются значения версий объектов, почему они могут резко увеличиваться, как сравнить две версии для определения хронологии изменений и как программно преобразовать эту строку в привычное десятичное число на встроенном языке 1С.
Для того чтобы понять природу изменения версий, заглянем под капот платформы 1С и посмотрим, как эти данные представлены в базе данных. Для каждой таблицы объектов СУБД платформа автоматически создает системное поле с именем _Version.
Особенности хранения зависят от используемой СУБД:
rowversion (в старых версиях SQL Server он назывался timestamp). Это бинарное число размером 8 байт (64 бита), которое автоматически увеличивается базой данных при любой операции вставки или обновления строки.rowversion нет. Поэтому платформа 1С эмулирует это поведение собственными механизмами, а само поле хранится в виде массива байт с типом данных bytea.Таким образом, на уровне базы данных версия — это обычное 8-байтовое число, которое при передаче в код 1С преобразуется в строку формата Base64 для удобства работы и унификации.
Проводя тесты в абсолютно чистой или монопольно запущенной тестовой базе данных, мы можем заметить, что при каждом изменении объекта значение версии увеличивается ровно на единицу. Например:
Версия 1: AAAAAB32JcE=
Версия 2: AAAAAB32KUA=
Однако в реальной рабочей базе данных при последовательном изменении одного и того же элемента (например, номенклатуры) мы можем увидеть резкие скачки. Разница между значениями может составлять сотни и тысячи единиц. Разберем, почему это происходит.
Счетчик версий (rowversion в SQL Server) является глобальным для всей базы данных, а не локальным для конкретной строки или таблицы. Это означает следующее:
Выясним, можно ли определить хронологическую последовательность изменений объекта по значению его версии. Да, это сделать очень просто.
Поскольку версия — это последовательно увеличивающееся число, приведенное к формату Base64, для определения хронологии достаточно сравнить строковые представления версий. В Base64 символы упорядочены по возрастанию их двоичного кода (где символ А эквивалентен нулю).
Правило простое: чем больше строка лексикографически (или при посимвольном сравнении), тем старше версия и тем позже был изменен объект. Сравнение строк работает абсолютно корректно и полностью отражает хронологию изменений на уровне СУБД.
Рассмотрим, как преобразовать строковое представление версии данных (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С при работе с новыми, еще ни разу не записанными в базу данных объектами. В этот момент у них еще нет физической записи в СУБД, а значит, и счетчик версии для них не определен.
ВерсияДанных для нового объекта является строка из 12 пробелов.Понимание работы реквизита ВерсияДанных помогает осознать, как платформа 1С реализует механизм оптимистической блокировки. Этот механизм защищает данные от коллизий, когда два пользователя одновременно открывают и редактируют один и тот же элемент.
Разобьем этот процесс по шагам:
AAAAAB32PyY=).UPDATE), проверяя в секции WHERE соответствие идентификатора объекта и его версии:
WHERE _IDRRef = @Идентификатор AND _Version = @КэшированнаяВерсия
Запись успешно проходит, а глобальный счетчик СУБД автоматически присваивает строке новую версию (например, AAAAAB32Q0Y=).UPDATE, где в секции WHERE по-прежнему указана старая версия AAAAAB32PyY=.AAAAAB32Q0Y=).Реквизит ВерсияДанных доступен разработчикам для чтения во встроенном языке и запросах. Однако, поскольку это системное и виртуальное поле, на него накладываются некоторые важные ограничения в языке запросов 1С:
СГРУППИРОВАТЬ ПО._Version.Используя описанные методы работы с версиями данных, вы сможете легко отслеживать изменения объектов, строить отчеты по хронологии модификации записей и эффективно отлаживать механизмы синхронизации и оптимистических блокировок в ваших информационных базах 1С.