Здравствуйте, форумчане.

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

```1c
    Функция СоздатьЧисловойГУИД(Спр) Экспорт
    ГУИД = ";
    Строка = "0123456789";
    ГЧС = Новый ГенераторСлучайныхЧисел();
    Пока СтрДлина(ГУИД)<=16 Цикл
    ГУИД = ГУИД + Сред(Строка, ГЧС.СлучайноеЧисло(0,СтрДлина(Строка)), 1);
    КонецЦикла;

    Если ПоискЧисловогоГУИДа(Строка(ГУИД), Спр) = Ложь Тогда
    Возврат ГУИД;
    Иначе
    СоздатьЧисловойГУИД(Спр);
    КонецЕсли;
    КонецФункции
```

Способ 2:

```1c
    Функция СоздатьЧисловойГУИД(Спр) Экспорт
    Попытка
    ScrptCtrl = новый COMОбъект("MSScriptControl.ScriptControl");
    ScrptCtrl.Language="javascript";
    Код = "function randomInteger(min, max){
    |var rand = min - 0.5 + Math.random() * (max - min + 1);
    |rand = Math.round(rand);
    |return rand;
    |}";
    ScrptCtrl.AddCode(Код);
    //СтрокаВыполнения = "randomInteger(" + мин + "," + СтрЗаменить(макс, Символы.НПП, ") + ")";
    СтрокаВыполнения = "randomInteger(0, 9999999999999999)";
    Ответ = ScrptCtrl.Eval(СтрокаВыполнения);
    УИДСтрокой = СтрЗаменить(Ответ, Символы.НПП, ");
    Если СтрДлина(УИДСтрокой) < 16 Тогда
    Для сч = 0 По 16 - СтрДлина(УИДСтрокой) - 1 Цикл
    УИДСтрокой = "0" + УИДСтрокой;
    КонецЦикла;
    КонецЕсли;

    Если ПоискЧисловогоГУИДа(УИДСтрокой, Спр) = Ложь Тогда
    Возврат УИДСтрокой;
    Иначе
    СоздатьЧисловойГУИД(Спр);
    КонецЕсли;
    Исключение
    СоздатьЧисловойГУИД(Спр);
    КонецПопытки;
    КонецФункции
```

Функция поиска:

```1c
    Функция ПоискЧисловогоГУИДа(ГУИД, Спр) Экспорт

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ "+Спр+".УИД
    |ИЗ
    | Справочник."+Спр+" КАК "+Спр+"
    |ГДЕ
    | "+Спр+".УИД = &УИД";

    Запрос.УстановитьПараметр("УИД", ГУИД);

    РезультатЗапроса = Запрос.Выполнить();

    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

    Если (ВыборкаДетальныеЗаписи.Количество() > 1) Тогда
    Возврат Истина;
    ИначеЕсли (ВыборкаДетальныеЗаписи.Количество() = 0) Тогда
    Возврат Ложь;
    Иначе
    Возврат Истина;
    КонецЕсли;

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

Есть кое-какие вещи излишние, например, ряд условий. На текущий момент все находится в тесте, на боевой базе не используется, но даже в тестовой базе умудряются появиться дубли.

Помогите, пожалуйста, что не так?
---
все намного проще:

```1c
    Гуид = Формат(ТекущаяУниверсальнаяДатаВМиллисекундах(), "ЧЦ=16; ЧВН=; ЧГ=0");
```

Если уже есть коды, то сделать проверку не сложно.

Если уже имеющиеся коды не сильно завязаны на конкретные значения, то можно их все перегенерировать по новой системе.
---
А длины поля в справочнике хватает? и нет ли лишних символов в кодах?
---
Хватает, лишних символов нет, функции смотрите
---
чем код справочника не устраивает ? Это ведь и есть по своей сути уникальный идентификатор, ну сделайте его 16 значным и все.
А генератор случайных чисел, - это немного не то...Случайный - не значит уникальный.
---
При удалении данных такой идентификатор больше не должен встретиться
---
а этот идентификатор должен быть случайным и неупорядоченным?
---
Должен быть случайным и неупорядоченных в пределах справочника
---
Используйте поле с типом значения УникальныйИдентификатор()
Ну и заполняйте его:
Спр.Ид = Новый УникальныйИдентификатор();
---
Только 16 символов, 36 мне не надо, плюс там есть "-".
---
как вариант можно самодельную функцию присобачить, с привязкой ко времени.
генератором двигаете знаков 8-10, и добавляете количество , например минут с определенной точки отсчета...н-р, начало этого года.
В году 525600минут в одном году, лет на 10 запас сделать и все
---
Вот самое интересное, что уникальности алгоритм позволяет добиться так как после генерации происходит проверка на существование в базе. Вот тут проблема и кроется. Проходит неделя две и появляется дубль в базе. Запрос проверяет наличие каждый раз, но все же получается что платформа в запросе даёт нулевой результат, следовательно создаётся ещё один ИД, который в базе есть. Только как я не понимаю?
---
```1c
    УИ = ТекущаяУниверсальнаяДатаВМиллисекундах();
```
---
Создаешь новый справочник. Тип номера - числовой. Длина номера - 16.
В твой справочник добавляешь реквизит с типом созданного справочника.
Ну и при записи создаешь новый элемент справочника и записываешь ссылку на него в добавленный реквизит.
---
Ещё вариант:
Создаешь регистр сведений, куда записываешь кучу заранее сгенерированных идентификаторов, а потом случайным образом из них выбираешь любой и удаляешь его из регистра.
Нагенерировать следующую порцию будет сложнее, зависит от того, как генерировалось изначально. Если были взяты числа подряд с начала, то надо будет просто помнить последнее число, и продолжить генерировать начиная с него. Иначе придется собирать числа, которые уже были использованы и удалены из регистра.
---
Ага, https://habrahabr.ru/post/324772/

(1) а при копировании элементов у вас точно новый гуид генерится?

Ну и лучше сразу несколько гуидов-кандидатов создать и сразу по ним всем проверить, чем рекурсиями и запросами в цикле заниматься.
---
копирование запрещено
---
Чтобы сгенерить случайное число, берите все имеющиеся значения запросом, помещайте их в таблицу значений, сортируйте. Далее, если нужен новый ГУИД, Берите последнее значение и добавляйте единицу.
Если нужно прямо генерировать, то сделайте следущим образом:
1. Таблицу значений нужно создавать с двумя колонками (ГУИД и КоличествоКопий. В запросе для "КоличествоКопий" ставите единицу - 1);
1. Создаете генерацию кода.
2. Добавляете в таблицу значений
3. Сворачиваете таблицу по ГУИД с суммированием колонки "КоличествоКопий".
4. Поиск в колонке КоличествоКопий = 2
5. Если неопределено, то вы создали уникальный код.
6. Если есть дубль, то генерировать новый не нужно. Делаете следующее:

а) Начинаете перебор коллекции (таблицы значений) с места, где был зафиксирован дубль
б) Прибавляете единицу к ГУИДу и сравниваете с текущим значением в итерации
в) До тех пор, пока не ГУИД не станет уникальным (если значений тьма, можно придумать функцию по поиску "пробела" между группой цифр).
---
Не знаю, я бы не заморачивался со всякими проверками...
смещение даты использовать да и все.
Текущая дата, в минутах, или секаундах - это по факту счетчик, который нам даст уникальность..ну и генератором генерим код. Потом соединяем, и получаем уникальное число, даже если генератор выдал повторяющийся код.
например,
5000001 минута+генератор выдал 2
5000002 минута генератор выдал 38
5000010 минута генератор выдал снова 2
но по факту число все равно уникальное.
500000102, 500000238,500001002

главное чтобы в одну и ту же минуту, у нас генератор не выдал одинаковое число..но тут вероятность много ниже, чем в разрезе недель месяцев или лет.
Хотя и с этим можно бороться..добавлять в конец числа текущую секунду
---
сделал еще 3-ий вариант через

ТекущаяУниверсальнаяДатаВМиллисекундах()

но когда получает значение 63 638 542 123 123
то приходится делать

СтрЗаменить(УИД, Символы.НПП, "");

А после добавлять нули в начале, так как длина строки не 16 символов
Проверка все равно нужна, так как в базе уже присутсвуют идентификаторы других элементов
УИД должен быть строковый.

Неудивлюсь, если через неделю увижу дубли.
---
все намного проще:

```1c
    Гуид = Формат(ТекущаяУниверсальнаяДатаВМиллисекундах(), "ЧЦ=16; ЧВН=; ЧГ=0");
```

Если уже есть коды, то сделать проверку не сложно.

Если уже имеющиеся коды не сильно завязаны на конкретные значения, то можно их все перегенерировать по новой системе.
---
Формат(ТекущаяУниверсальнаяДатаВМиллисекундах(), "ЧЦ=16; ЧВН=; ЧГ=0");
Я думаю этот способ попробую. Спасибо
---
создайте вспомогательный независимый регистр с измерением - ГУИД. При создании элемента справочника добавляйте запись в регистр, при удалении - не удаляйте.
Вообще странно, что рандом на 16-значном числе выдает дубли. Значит, кривой какой-то алгоритм рандома.
---
Почему просто нельзя вести числовую последовательность и при записи нового элемента выбирать запросом последний номер и прибавлять единицу ?
---
```1c
    Функция СоздатьЧисловойГУИД(Спр) Экспорт
    УИД = Формат(ТекущаяУниверсальнаяДатаВМиллисекундах(), "ЧЦ=16; ЧВН=; ЧГ=0");
    Если Не Справочники[Спр].НайтиПоРеквизиту("УИД", УИД) = Справочники[Спр].ПустаяСсылка() Тогда
    СоздатьЧисловойГУИД(Спр);
    Иначе
    Возврат УИД;
    КонецЕсли;
    КонецФункции
```

Сделал таким образом.