Генерация уникального ключа набора данных

12.03.25

Разработка - Универсальные функции

Детерминированный ключ - это такой ключ, который всегда вычисляется одинаково при одних и тех же входных данных.

Добрый день! 

Генерация уникального детерминированного ключа набора данных - это процесс создания уникального идентификатора, который можно однозначно связать с конкретным набором данных. Ключ должен быть детерминированным, то есть всегда одинаковым для одинаковых входных данных, чтобы гарантировать стабильность и консистентность идентификации данных.

Для чего это нужно программисту 1С? Я использовал этот алгоритм для генерации уникальных идентификаторов строк табличной части в документе. В данном документе строки ТЧ группируются по 11 реквизитам, что исключает возможность добавления двух разных строк с одинаковым набором значений этих реквизитов. После вычисления детерминированного ключа, я уверен, что для одинакового набора данных ключ будет всегда одинаковым. Мне нужно было хранить дополнительную информацию по каждой строке документа в РС, и вместо того, чтобы делать 11 измерений, я сделал 2 - ссылка на документ и UUID строки, что позволило значительно упростить дальнейшую работу с ним.

В моей реализации генерируется UUID v5 для произвольного набора ссылок БД или строк.

Если вызывается UUID для ссылок, то сначала запросом выбираются их строковые представления УИД, а затем идет генерация ключа.

 
 Моя реализация

 

// Функция - Уникальный идентификатор набора ссылок. Ключ записи.
//
// Параметры:
//  СписокUUID     - Массив Из Ссылка - Массив со ссылками на объекты БД
//    ВозвращатьКакУИД    - Булево - определяет, возвращать ли результат как УникальныйИдентификатор. Если Ложь - возвращается строкой
//
// Возвращаемое значение:
//  Строка, УникальныйИдентификатор - Ключ записи для некоторого набора ссылок
//
Функция УникальныйИдентификаторНабораСсылок(СписокСсылок, ВозвращатьКакУИД = Ложь) Экспорт

    // получаем UUID строкой по ссылкам
    ЧастиЗапроса = Новый Массив;
    ЧастьЗапросаВыборка =
    "ВЫБРАТЬ
    |    ПРЕДСТАВЛЕНИЕ(УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Ссылка))
    |ИЗ
    |    %ИМЯ ТАБЛИЦЫ%
    |ГДЕ
    |    Ссылка В (&СписокСсылок)
    |";

    ПерваяСсылка = Истина;
    Для Каждого Ссылка Из СписокСсылок Цикл

        Если Не ПерваяСсылка Тогда
             ЧастиЗапроса.Добавить("
                                     |    ОБЪЕДИНИТЬ ВСЕ
                                    |
                                    |");
        КонецЕсли;

        ПолноеИмяМетаданных = Ссылка.Метаданные().ПолноеИмя();

        ЧастиЗапроса.Добавить(СтрЗаменить(ЧастьЗапросаВыборка, "%ИМЯ ТАБЛИЦЫ%", ПолноеИмяМетаданных));

        ПерваяСсылка = Ложь;

    КонецЦикла;

    Запрос = Новый Запрос;
    Запрос.Текст = СтрСоединить(ЧастиЗапроса, "");
    Запрос.УстановитьПараметр("СписокСсылок", СписокСсылок);

    Возврат УникальныйИдентификаторНабораСтрок(Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку(0), ВозвращатьКакУИД);

КонецФункции

// Функция - Уникальный идентификатор набора строк. Ключ записи.
//
// Параметры:
//  СписокUUID     - Массив Из Строка - Массив с произвольными строками
//    ВозвращатьКакУИД    - Булево - определяет, возвращать ли результат как УникальныйИдентификатор. Если Ложь - возвращается строкой
//
// Возвращаемое значение:
//  Строка, УникальныйИдентификатор - Ключ записи для некоторого набора строк
//
Функция УникальныйИдентификаторНабораСтрок(СписокСтрок, ВозвращатьКакУИД = Ложь) Экспорт

    СписокДляСортировки = Новый СписокЗначений;
    СписокДляСортировки.ЗагрузитьЗначения(СписокСтрок);
    СписокДляСортировки.СортироватьПоЗначению(НаправлениеСортировки.Возр);
    СписокСтрок = СписокДляСортировки.ВыгрузитьЗначения();

    СтрокаОбщая = СтрСоединить(СписокСтрок, ",");

    Хеширование = Новый ХешированиеДанных(ХешФункция.SHA1);
    Хеширование.Добавить(СтрокаОбщая);

    ДвоичныеДанные = Хеширование.ХешСумма;
    Хеш = НРег(ПолучитьHexСтрокуИзДвоичныхДанных(ДвоичныеДанные));	
	
    // Преобразуем хеш в массив байтов (берем первые 16 байтов)
    МассивБайтов = Новый Массив;
    Сч = 1;
    Пока Сч < 32 Цикл
        Байт = Сред(Хеш, Сч, 2);
        МассивБайтов.Добавить(Байт);
        Сч = Сч + 2;
    КонецЦикла;

	// Версия UUID 5
	МассивБайтов[6] = СтрШаблон("5%1", Прав(МассивБайтов[6], 1));
	
	// Вариант RFC 4122
	ДесятичноеЧисло = ЧислоИзШестнадцатеричнойСтроки(СтрШаблон("0x%1", МассивБайтов[8]));
	
	БезДвухБитов = ПобитовоеИ(ДесятичноеЧисло, 63); // обнуляем старшие 2 бита, 63 = 0011 1111
	ИтоговыйБайт = ПобитовоеИли(БезДвухБитов, 128); // записываем в старшие 2 бита 10, 128 = 1000 000 
	
	СимволыHEX = "0123456789abcdef";
	СтаршийРазряд = Сред(СимволыHEX, Цел(ИтоговыйБайт / 16) + 1, 1);
	МладшийРазряд = Сред(СимволыHEX, (ИтоговыйБайт % 16) + 1, 1);
	
	МассивБайтов[8] = СтаршийРазряд + МладшийРазряд;
	
	UUIDБезРазделителей = СтрСоединить(МассивБайтов, "");
	
	ИтоговыйUUID = СтрШаблон("%1-%2-%3-%4-%5",
		Сред(UUIDБезРазделителей, 1, 8),
		Сред(UUIDБезРазделителей, 9, 4),
		Сред(UUIDБезРазделителей, 13, 4),
		Сред(UUIDБезРазделителей, 17, 4),
		Сред(UUIDБезРазделителей, 20, 12));

	Если Не СтроковыеФункцииКлиентСервер.ЭтоУникальныйИдентификатор(ИтоговыйUUID) Тогда
	    ВызватьИсключение "Не удалось получить уникальный идентификатор из входных данных. Строка " + ИтоговыйUUID + " не отвечает требованиям УИД";
	КонецЕсли;
	
    Если ВозвращатьКакУИД Тогда
        ИтоговыйUUID = Новый УникальныйИдентификатор(ИтоговыйUUID);
    КонецЕсли;

    Возврат ИтоговыйUUID;

КонецФункции

Единственный БСП-шный вызов:

Если Не СтроковыеФункцииКлиентСервер.ЭтоУникальныйИдентификатор(ИтоговыйUUID) Тогда
        ВызватьИсключение "Не удалось получить уникальный идентификатор из входных данных. Строка " + ИтоговыйUUID + " не отвечает требованиям УИД";
КонецЕсли;

Поэтому если эта проверка не нужна, то можно вырезать.

Вступайте в нашу телеграмм-группу Инфостарт

UUID уникальный идентификатор ключ записи

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    3318    Abysswalker    7    

44

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

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

14.05.2025    6169    DeerCven    15    

57

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Благодаря этим пяти строчкам можно больше не заморачиваться с загрузкой из внешних файлов. Пользуюсь везде, всегда и постоянно.

21.05.2024    48208    dimanich70    83    

169

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    7257    6    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    60141    atdonya    31    

69

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    9034    ke.92@mail.ru    17    

68
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. dsdred 4166 12.03.25 13:18 Сейчас в теме
А хеш чем не годится?
Берешь таблицу, стерилизуешь ее в текст и получаешь хеш текста.
Можно и по строкам так сделать.
SerVer1C; +1 Ответить
3. poor_developer 25 12.03.25 13:50 Сейчас в теме
(1) Мы не ищем легких путей) А если серьезно, хотел получать именно УИД, чтобы было какое-то однообразие с типовым, что ли. Во внутреннем потреблении, как пример, идентификатор строки УИДом записывается
4. dsdred 4166 12.03.25 13:52 Сейчас в теме
(3) Ясно. за подвиги плюсик поставлю ))
poor_developer; +1 Ответить
2. Diversus 2337 12.03.25 13:27 Сейчас в теме
В догонку:

// Уникальный идентификатор по произвольной строке.
// 
// Параметры:
//  Строка - Строка - Произвольная строка (можно не УникальныйИдентификатор)
// 
// Возвращаемое значение:
//  УникальныйИдентификатор - Уникальный идентификатор по произвольной строке
Функция УникальныйИдентификаторПоПроизвольнойСтроке(Знач Строка) Экспорт
	
	// BSLLS:MagicNumber-off
	Хеш = НРег(MD5(Строка));
	СтрокаУникальныйИдентификатор = СтрШаблон("%1-%2-%3-%4-%5", 
		Лев(Хеш, 8),
		Сред(Хеш, 9, 4),
		Сред(Хеш, 13, 4),
		Сред(Хеш, 17, 4),
		Прав(Хеш, 12));
	// BSLLS:MagicNumber-on
	
	Возврат Новый УникальныйИдентификатор(СтрокаУникальныйИдентификатор);
	
КонецФункции

// Возвращает хеш-сумму по алгоритму MD5.
// 
// Параметры:
//  Данные - Строка, ДвоичныеДанные - Исходная строка.
// 
// Возвращаемое значение:
//  Строка - Хеш-сумма MD5
Функция MD5(Знач Данные) Экспорт
	
	Возврат ХешСтроки(ХешФункция.MD5, Данные);			    
	
КонецФункции

Функция ХешСтроки(Алгоритм, Строка)
	
	Хеш = Новый ХешированиеДанных(Алгоритм);
	Хеш.Добавить(Строка);	
	Возврат СтрЗаменить(Строка(Хеш.ХешСумма), " ", "");
				    	
КонецФункции
Показать
poor_developer; +1 Ответить
5. SlavaKron 12.03.25 14:05 Сейчас в теме
(2)
Лев(Хеш, 8),
Сред(Хеш, 9, 4),
Сред(Хеш, 13, 4),
Сред(Хеш, 17, 4),
Прав(Хеш, 12));
По какому принципу формируете УИД из 16-байтового хеша? В 1С, например, другой порядок.
Функция ПолучитьХешКлючаАналитики(ТаблицаЗначенийАналитик) Экспорт
	
	ТаблицаЗначенийАналитик.Сортировать("Аналитика", Новый СравнениеЗначений);
	
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.MD5);
	
	Для Каждого Стр Из ТаблицаЗначенийАналитик Цикл
		
		ХешированиеДанных.Добавить(ПолучитьДвоичныеДанныеИзСтроки(XMLСтрока(Стр.Аналитика)));
		ХешированиеДанных.Добавить(ПолучитьДвоичныеДанныеИзСтроки(XMLСтрока(Стр.Значение)));
		
	КонецЦикла;
	
	Стр = СтрЗаменить(ХешированиеДанных.ХешСумма, " ", "");
	ЧастиУИД = Новый Массив;
	ЧастиУИД.Добавить(Прав(Стр, 8));
	ЧастиУИД.Добавить(Сред(Стр, 21, 4));
	ЧастиУИД.Добавить(Сред(Стр, 17, 4));
	ЧастиУИД.Добавить(Лев(Стр, 4));
	ЧастиУИД.Добавить(Сред(Стр, 5, 12));
	
	Возврат Новый УникальныйИдентификатор(СтрСоединить(ЧастиУИД, "-"));
	
КонецФункции
Показать
poor_developer; +1 Ответить
6. poor_developer 25 12.03.25 14:12 Сейчас в теме
(5) Прикольно. Я читал, что для детерминированного ключа используют алгоритм формирования UUID v5, а он хеширование входных данных по SHA1 использует
7. Diversus 2337 12.03.25 14:23 Сейчас в теме
(5) На мой взгляд порядок не важен.
Самое главное, чтобы пропустив через эту функцию, возвращался один и тот же GUID.
Грубо говоря: для строки "привет" всегда возвращался GUID "cdaa7bbf-7f19-418d-bda0-c5511417eb6f", ну и для другой строки измененной минимально, другой GUID. То, как формирует GUID 1С в данном случае не важно. Да, это будет не GUID v4/v5, но для своих задач, которые могут понадобиться это точно будет работать.
8. Cyberhawk 137 14.03.25 05:19 Сейчас в теме
Возврат УникальныйИдентификаторНабораСтрок(Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку(0)
Получается, алгоритм оперирует только УИДами (без типов)?

СписокДляСортировки.СортироватьПоЗначению(
Из-за этого будет коллизия, когда в ТЧ будет две каких-нибудь колонки разных типов, но элементы которых имеют попарно одинаковые УИДы (например, Серия1 + Характеристика1 дадут УИД1+УИД2, а Серия2 + Характеристика2 дадут УИД2+УИД1, а ваш алгоритм посчитает это за одинаковый входной набор).
9. poor_developer 25 14.03.25 07:51 Сейчас в теме
(8)
Получается, алгоритм оперирует только УИДами (без типов)?

В итоге все равно крутится все в строках. По сути первый метод для удобства формирования по одной строке ТЧ, чтобы вытянуть УИДЫ по ссылкам и передать в метод УникальныйИдентификаторНабораСтрок(СписокСтрок, ВозвращатьКакУИД = Ложь). Ничто не мешает вызывать этот метод для произвольных строк, а не УИДов

СписокДляСортировки.СортироватьПоЗначению(

На мой взгляд это нужно, чтобы в разных местах не держать в голове порядок т.н. измерений. В одном месте сформирую по значениям Номенклатура+Характеристика+Назначение, в другом "Характеристика+Номенклатура+Назначение" - ключ будет одинаковый.

имеют попарно одинаковые УИДы (например, Серия1 + Характеристика1 дадут УИД1+УИД2, а Серия2 + Характеристика2 дадут УИД2+УИД1


Вы полагаете, что есть вероятность, что УИД характеристики 1 будет равен УИДу серии 2? Из примера не совсем понял.

В любом случае количество входных данных нужно определять для каждого документа отдельно. Может где-то этот подход вообще не выйдет применить.
10. Cyberhawk 137 14.03.25 08:03 Сейчас в теме
полагаете, что есть вероятность, что УИД характеристики 1 будет равен УИДу серии 2?
Да. При обменах, например, это вообще штатная ситуация, если один объект источника превращается в несколько объектов приемника (например, один документ источника - в три разных вида документов в приемнике).
количество входных данных нужно определять для каждого документа отдельно. Может где-то этот подход вообще не выйдет применить
Если ключ уникальности создавать не только из самих УИДов, но еще и из типов ссылок, то должно получиться гарантированно универсально.
Для отправки сообщения требуется регистрация/авторизация