Быстрое получение уникального числового значения без блокировок

13.08.14

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

Столкнулся с проблемой блокировок/тормозов при назначении уникального Штрихкода.
Работало через поиск Макс. значения в Регистре сведений и записи туда нового значения.
В принципе данный функционал можно использовать для создания уникальныхзначений.
Только для Клиент-серверного режима работы. Только для SQL Server, но думаю что похожий функционал можно сделать и на Oracle/Postgre

Решение созрело такое:
1. Создается табличка прямо в БД 1С вида:
(для MS SQL Server)
CREATE TABLE [dbo].[zbarcodes](
    [ID] [bigint] IDENTITY([НАЧАЛЬНОЕЗНАЧЕНИЕСЧЕТЧИКА],1) NOT NULL,
 CONSTRAINT [PK_zbarcodes] PRIMARY KEY CLUSTERED
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

[НАЧАЛЬНОЕЗНАЧЕНИЕСЧЕТЧИКА]  - заменить на число, например 20000000000 - первый шк в системе (или другое число - последнее в Вашей учетной системе, если хочется продолжить текущую нумерацию)

2. Создаем Процедурку в общем модуле с выполнением на сервере (для УФ)

//Процедура получения уникального штрихкода (у нас используется Code 128 - без контрольных символов)
//Параметры:
//Организация - можно сделать таблицы на каждую организацию - и получать по организации имя таблицы в БД из ЗначенияСвойствОбъектов
//СтруктураПараметров - параметры подключения к БД SQL через ADO

Функция ПолучитьНовыйШтрихкод(Организация, СтруктураПараметров) Экспорт
   
   Соединение= Новый COMObject("ADODB.Connection");
    Соединение.ConnectionString = "Driver={SQL Server};Server=" + СокрЛП(СтруктураПараметров.Сервер)
                                                        + ";UID=" + СокрЛП(СтруктураПараметров.Логин)
                                                        + ";pwd=" + СокрЛП(СтруктураПараметров.Пароль)
                                                        + ";Database=" + СокрЛП(СтруктураПараметров.БД);
    Соединение.ConnectionTimeOut = 40;
    Соединение.CommandTimeout = 0;
    Соединение.CursorLocation = 3;
    Попытка
        Соединение.Open();
    Исключение
        #Если Клиент Тогда
        Сообщить("Возникла ошибка подключения к базе");
        #КонецЕсли
        Соединение="";
        Возврат Неопределено;
    КонецПопытки;

        ТекстЗапроса="INSERT into zbarcodes DEFAULT VALUES";
        Соединение.Execute(ТекстЗапроса);   
       
        ТекстЗапроса="SELECT @@IDENTITY";
        Выборка=Соединение.Execute(ТекстЗапроса);       
       
        НовыйШтрихкод=Выборка.Fields(0).Value;
       
        Выборка.Close(); 
        Выборка =""
        Соединение = "";

    Возврат  НовыйШтрихкод;
   
КонецФункции

В итоге все забыли что такое блокировки при присвоении новых штрихкодов, и обеспечена уникальность штрихкода, + скорость получения значения!

счетчик блокировки

См. также

Вставляем картинку из буфера обмена (платформа 1С 8.3.24)

Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

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

1 стартмани

18.03.2024    2655    0    John_d    8    

53

GUID в 1С 8.3 - как с ними быть

Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

12.02.2024    4590    atdonya    22    

45

Переоткрытие внешних обработок

Универсальные функции Платформа 1С v8.3 Бесплатно (free)

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

30.11.2023    3950    ke.92@mail.ru    16    

61

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    8799    YA_418728146    6    

141

Печать непроведенных документов для УТ, КА, ERP. Настройка печати по пользователям, документам и печатным формам

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    2069    21    progmaster    7    

3

Расширение: Быстрые отборы через буфер [Alt+C] Копировать список, [Alt+V] Вставить список, [Ctrl+C] Копировать из файлов

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    16139    133    sapervodichka    112    

129

Система контроля ведения учета [БСП]

Универсальные функции Механизмы типовых конфигураций БСП (Библиотека стандартных подсистем) Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

В данном материале рассмотрим типовой алгоритм подсистемы контроля учета БСП в конфигурациях на примерах.

18.07.2022    7241    quazare    8    

109
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. yukon 143 13.08.14 12:17 Сейчас в теме
у нас используется Code 128

Так можно вообще не заморачиваться с базой данных и т.п.
Функция ПолучитьНовыйШтрихкод()
    Возврат СтрЗаменить(ВРег(Новый УникальныйИдентификатор()),"-", "");
КонецФункции
2. serferian 26 13.08.14 12:18 Сейчас в теме
ага, только ручками очень тяжко потом это всё набирать в случае отказа сканера ШК
3. pvase 401 13.08.14 12:28 Сейчас в теме
(2) serferian,
Если интересует без тормозов, то лучше создать массив уникальных значений а потом лишь их использовать. Не думаю что регистр сведений для этого лучшая реализация. Ведь требуется всего 3 поля, 1 - ID, 2 - значение, 3 - уже используется. Если вам подойдет на таблице SQL, то можете создать такую таблицу, заполнить ее значениями от 1 до 1000 000 000 и пользоваться ими.
5. jobkostya1c_ERP 100 13.08.14 13:00 Сейчас в теме
(3) pvase, обычно штрих-коды генерируют с целью записи в базу, так что тут только какая-то структура в ОЗУ на вкус разработчика (проиндексированная ТаблицаЗначений, Структура, соответствие. А вот при инициализации этого механизна и при окончании работы лучше всего ХранилищеЗначения. Ну еще может что-то быть для сохранения промежуточных результатов работы в базе.
6. serferian 26 13.08.14 13:48 Сейчас в теме
(3) pvase,
Не согласен что так лучше. Все-таки Identity - 100% уникальность, а галочки используется и прочее - это во первых поиск "без галочки", а во-вторых блокировки "на запись данного значения"! Да и цифры "чисто теоретически" могут закончится)))
4. jobkostya1c_ERP 100 13.08.14 12:56 Сейчас в теме
Или если уж очень нужна сквозная нумерация по группам то нужно переработать механизм записи штрих-кода: после успешной записи новой записи в регистр Штрихкоды для очередной номенклатуры записывать последнее значение в константу. При следующей генерации смотреть значение константы, увеличивать соответствующий разряд на единицу и писать в регистр.
А вот если нумерация сквозная, но сложная по каким-то группам (и прочим условиям), то нужно все хранить в ХранилищеЗначения и делать доступ наподобие стандартной функции чтения данных констант и учетной политики регистров как в стандартных конфигурациях:
Функция ПолучитьЗначениеПеременной(ИмяПараметра, Кэш = Неопределено, КэшИзменен = Ложь) Экспорт
	
	Если Кэш = Неопределено Тогда
		// Кэш не был проинициализирован
		Кэш = Новый Структура;
	Иначе
		// Ищем значение в структуре
		НайденноеЗначение = Неопределено;
		Если Кэш.Свойство(ИмяПараметра, НайденноеЗначение) Тогда
			Возврат НайденноеЗначение;
		КонецЕсли;
	КонецЕсли;
//Дальше куча условий по  ....
// В конце если ничего не нашли, то читаем из константы с переданным именем и записываем ее в нашу переменную "КЭШ".
// Следующий доступ к нужному параметру в рамках сеанса будет уже из ОЗУ до момента изменения в базе. Тогда уже будет автоматическое перезаполнение переменной в "КЭШ"

Иначе
		НайденноеЗначение = Константы[ВРег(ИмяПараметра)].Получить();
		
		//СтрокаИсключения = "Невозможно обработать параметр " + """" + ИмяПараметра + """" + " для получения значения";
		//ВызватьИсключение СтрокаИсключения;
	КонецЕсли;
	
	Кэш.Вставить(ИмяПараметра, НайденноеЗначение);
	КэшИзменен = Истина;
	
	Возврат НайденноеЗначение;
	
КонецФункции
Показать

Эффективно, когда происходит очень частое обращение к данным малого объема. Например, восстановление последовательности документов или пакетный ввод документов. Заодно приведу и стандартную функцию установки значения переменной с учетом "КЭШ"а. Примечание: НЕ путать эдакое название переменной с обычным кэшем базы 1С, который является кучей файлов на диске.
Процедура УстановитьЗначениеПеременной(ИмяПараметра, Кэш, ЗначениеПараметра, ОбновлятьВоВсехКэшах = Ложь) Экспорт
	
	Если Кэш = Неопределено Тогда
		// Кэш не был проинициализирован
		Кэш = Новый Структура;
	КонецЕсли;
	
	Если Кэш.Свойство(ИмяПараметра) Тогда
		Кэш.Вставить(ИмяПараметра, ЗначениеПараметра);
	КонецЕсли;
	
	#Если Клиент ИЛИ ВнешнееСоединение Тогда
		Если ОбновлятьВоВсехКэшах Тогда
			КэшНаСервере = ПараметрыСеанса.ОбщиеЗначения.Получить();
			КэшНаСервере.Вставить(ИмяПараметра, ЗначениеПараметра);
			ПараметрыСеанса.ОбщиеЗначения = Новый ХранилищеЗначения(КэшНаСервере);
		КонецЕсли;
	#КонецЕсли
	
КонецПроцедуры
Показать
7. serferian 26 13.08.14 13:51 Сейчас в теме
(4) kostyaomsk,
С константой вариант тоже не прошел - по причине блокировок! Пробовали!
С Кэшем интересно будет попробовать.



8. jobkostya1c_ERP 100 13.08.14 21:02 Сейчас в теме
Про уникальность по методу
Новый УникальныйИдентификатор;
с дальнейшей любой обработкой даже незначительно снижающей разрядность полученного числа.
Я представлял задачу по-другому:
1. Где-то сидят 20 операторов (по всему городу и набивают номенклатуру, а заодно и штрихкоды
2. В 1С из различных источников загружается что-попало из номенклатуры и все это надо "пометить" штрихкодами
3. Комбинация 1 и 2 + еще все что угодно + уже вышедшая из употребления (продажи, обработки) номенклатура
Причем, в длинных штрихкодах (не EAN13) нужно и тип товара проклассифицировать (весовой, штучный) и кучу "префиксов" предусмотреть (код региона, магазина, оператора). Еще и дополнительные штрих-коды могут быть для отличия своего товара от чужого (при попытке недобросовестного покупателя вернуть товар).
Причем, еще требование на сквозную нумерацию в пределах региона-магазина-оператора (было такое в практике по учету основных средств холдинга в консолидированной отчетности).
Это что касается "пометки".
А вот чтение должно осуществляться максимально быстро. Для этого по ночам регламентные задания должны перезаписывать блоками в порядке возрастания нужные штрихкода, и обновлять индексы для поиска по регионам-магазинам как для аппаратуры POS-терминала, так и для консолидированного товарного отчета.
Такой вот гипотетический пример, который все в себя включает.
Для поиска тут только оптимизированный запрос на SQL сервер. А вот для модификации: перед записью на компьютере "оператора штрихкодов" уже должны в ОЗУ быть прочитаны и его часть сложной структуры данных к которой должен быть доступ без блокировок с возможностью записи, т.к. тут предусмотрена нумерация в пределах сегмента.
Иначе как-то так:
Процедура ПолучитьНовыйШтрихкод(Регион, магазин, КодОператора, СписокНоменклатуры, ТабНом)    
    // ТабНом - таблица значений с колонками номенклатура и штрихкод
    Для Каждого Ном Из СписокНоменклатуры Цикл
       ЧастьШК = Лев(СтрЗаменить(ВРег(Новый УникальныйИдентификатор()),"-", ""), N); // уникальная часть ШК. 3 < N << 32
       НоваяСтрока = ТабНом.Добавить();
       НоваяСтрока.Номенклатура = Ном;
       НоваяСтрока.ШтрихКод = Регион + Магазин + КодОператора + ЧастьШК; 
    КонецЦикла;
    //... Что-то такое для сортировк и добавления индексов...
    ТабНом.Сортировать(...
КонецПроцедуры
Показать

От задачи зависит.
9. danila_inf 20.08.14 12:09 Сейчас в теме
Задумка хороша. Вижу явное ограничение в следующем.
Запись идет во внешний источник данных.
Использование транзакции для сохранение целостности данных между 1с(таблицами sql связанные с 1с) и sql(независимая таблица от 1с) невозможно. При получении штрихкода
НовыйШтрихкод=Выборка.Fields(0).Value
транзакция в сиквел завершена, транзакция в 1с(если была начата) не завершена.
При аварийном завершении программы(возможны разные причины) счетчик будет увеличен только в сиквел.
Если задача по сквозной нумерации не стоит то задача решена на 5)).
10. serferian 26 20.08.14 12:27 Сейчас в теме
(9) danila_inf,
При аварийных завершениях 1С - просто получаем пропуск данного ШК.
12. jobkostya1c_ERP 100 27.08.14 16:45 Сейчас в теме
(9) danila_inf, более точно по-научному написал суть проблем. Хотелось бы узнать, можно ли как то усовершенствовать алгоритм чтоб, допустим сом-процесс был один и уже пусть на него идут запросы со всех сторон. Вернее будет хоть какая-то очередь транзакций со стороны 1С.
11. jobkostya1c_ERP 100 27.08.14 16:41 Сейчас в теме
Идея использования сервера SQL опять не по назначению (или сначала не по назначению для поиска максимального значения во внутренней таблице соответствующей на низком уровне регистру сведений 1С 8 путем вставки новой строки и последующего автоинкремента +1
Насчет скорости исполнения вопрос:
Функция ПолучитьНовыйШтрихкод(Организация, СтруктураПараметров) Экспорт
   ... 
   Соединение= Новый COMObject("ADODB.Connection"); // И вот это в цикле каждый раз!
   ...
КонецФункции;

И везде (раз подняли тему блокировок) SQL сервер атакую со всех сторон из 1С данная функция, каждый раз: создание COM-объекта, логин, пароль, авторизация, максимальный таймаут? А еще у комовских объектов бывает метод Close() не срабатывает.
А если нужно массив сгенерировать штрихкодов и записать. Вот то, что запрос через Опять цикл вызова функции ПолучитьНовыйШтрихкод?
А еще вызов самого метода COM-объекта? Для получения одного-единственного значения.
Мы в свое время использовали запросы 1С в SQL-серверном варианте вида:
ВЫБРАТЬ ПЕРВЫЕ 1
	Рег.Код КАК Код,
	Рег.СтрИнфо КАК СтрИнфо
ИЗ
	РегистрСведений.РегистрКодов КАК Рег

ДЛЯ ИЗМЕНЕНИЯ
	РегистрСведений.РегистрКодов

УПОРЯДОЧИТЬ ПО
	СтрИнфо УБЫВ,
	Рег УБЫВ
Показать

Если нужно записать за раз много данных, то получим последнее максимальное значение в нужной группе, а дальше генерация кодом таблицы значений для записи (примитивный алгоритм +1 с преобразованиями в строку) и дальше блокировка регистра и запись опять через 1С.
А в Вашем алгоритме получается полное отсутствие блокировок в момент записи строки что плюс, но вот сразу идет запись в базу нового штрихкода самим SQL-сервером. А если произойдет ошибка уже дальше в коде. Запрос то в транзакции записи и тот и другой уже отработал?
Или потом блок
Попытка-исключение
. Как сработает пропуск штрихкода?
13. jobkostya1c_ERP 100 27.08.14 20:47 Сейчас в теме
Вот нашел в статье Спускаемся в 1С 8.2 на уровень Базы Данных (Часть2) подробно про доступ посредством СУБД MS SQL Server, и в частности про оптимизацию подключения:
...для оптимизации функцию получения объекта ADODB.Connection можно разместить в общем модуле, в настройках которого выставлено «Повторное использование». Это позволит не создавать каждый раз новый объект подключения, а будет использоваться уже созданный объект. В теории это позволит сократить время вызова соединения, а так же совсем чуть-чуть сэкономит ресурсы системы
.
14. mixsture 31.10.14 16:15 Сейчас в теме
Мне кажется некорректным сравнение производительности между полноценным регистром сведений, который хранит явно больше информации с голой таблицей из цифр.

Регистр сведений:
-хранит еще ссылки на номенклатуру, характеристики, иногда серии. Плюс индексы по этому всему, которые ускоряют поиск, но замедляют запись (и, соответственно, блокировку)
-отлично работает с РИБ (да, у вас не получится с РИБ единой нумерации при одновременной работе пользователей в разных узлах, но сам обмен информацией работает на пять)
-Не стоит забывать о резервных копиях - сделав голую таблицу, вы добавили головной боли сисадмину - ему теперь все это нужно помнить и заодно делать бекап вместе с базой 1с. Опоздал на несколько минут - получай рассинхронизированную копию базы 1с и таблицы. А что делать, если нужно развернуть копию? Забыли про это - и она тоже будет увеличивать счетчик в таблице для боевой?
-В базе, возможно, куча подписок на события, сквозь которые тоже проходит выполнение при записи в этот регистр.

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

Да и избыточная по времени блокировка как правило получается из-за того, что между получением значения и записью вставляют массу другой логики. Например, выбрали для изменения записи регистра, затем проводимся по кучи других регистров, масса проверок и вот в конце снимаем блокировку при фиксации транзакции. Так приближаем максимально чтение, запись и снятие блокировки - вот и нет их.
15. serferian 26 31.10.14 16:40 Сейчас в теме
-отлично работает с РИБ (да, у вас не получится с РИБ единой нумерации при одновременной работе пользователей в разных узлах, но сам обмен информацией работает на пять)


угу еще один недостаток - когда идет обмен РС полностью уходит в блокировку!!! и спрашивается - зачем оно нам?
(правда этот механизм нужен только в центральной базе!)
ну и выход для РИБ тоже есть через префиксы - их никто не отменял.

-Не стоит забывать о резервных копиях - сделав голую таблицу, вы добавили головной боли сисадмину - ему теперь все это нужно помнить и заодно делать бекап вместе с базой 1с. Опоздал на несколько минут - получай рассинхронизированную копию базы 1с и таблицы. А что делать, если нужно развернуть копию? Забыли про это - и она тоже будет увеличивать счетчик в таблице для боевой?


тут уж как настроите подключение к БД - если копия - то данные о БДSQL можно получать из настроек БД 1C
а копии однозначно и давно создаются при помощи SQL Backup. вряд ли я смогу 300Gb выгрузкой - загрузко данных копировать)) - так что табличка в ажуре и актуальна!

Константа отпала сразу на основе начала реальной работы!
16. mixsture 02.11.14 22:02 Сейчас в теме
(15) serferian, верно, поэтому я предлагаю использовать объект попроще, чем РС именно для задачи быстрого получения следующего номера. Пусть это будет 1 элемент справочника, в реквизите которого хранится последнее выданное число. Раз пропуски в номерах возможны => можно не связывать эту транзакцию с остальной логикой => читаем значение, делаем инкремент, записываем без пауз, а уже потом используем записанное значение. Я думаю, будет ненамного медленнее и особых блокировок не возникнет, зато все сделано средствами платформы и не добавляет мучений сисадминам, легко используется в остальных механизмах платформы.

тут уж как настроите подключение к БД - если копия - то данные о БДSQL можно получать из настроек БД 1C

Если вы хотите создавать таблицу внутри базы 1с - недостатков тоже порядочно: нарушаем лицензию 1с + конфигуратор легко может стереть нашу таблицу при загрузке.
Оставьте свое сообщение