В производственных и складских системах часто возникает задача поштучного учета продукции, когда каждому экземпляру присваивается собственный уникальный идентификатор или серийный номер формата Code 128. Иногда разработчики принимают решение сделать этот уникальный номер измерением регистра накопления остатков. Проанализируем, к каким последствиям для СУБД приводит такое архитектурное решение, как на самом деле работает таблица итогов при обнулении остатков и какими способами можно оптимизировать учет поштучной номенклатуры.
Основное опасение разработчиков при подобной схеме заключается в том, что со временем в таблице остатков образуются миллионы записей с нулевым количеством, по которым сканеру штрихкодов придется выполнять долгий поиск. Разберем, как платформа 1С хранит итоги регистра накопления остатков.
Физически регистр остатков в СУБД состоит из двух основных таблиц: таблицы движений и таблицы итогов. Когда по конкретному набору измерений (включая штрихкод) регистрируется расход, полностью списывающий ранее оприходованный остаток, текущий остаток становится равным нулю. В актуальном срезе итогов записи с нулевым остатком не сохраняются: платформа исключает нулевые комбинации измерений из актуального среза. Это означает, что при правильном обращении к актуальным остаткам платформа не выполняет поиск по миллионам закрытых штрихкодов.
Однако здесь кроется серьезная скрытая проблема — высокая кардинальность данных и механизм хранения срезов по периодам. Платформа 1С фиксирует промежуточные итоги с шагом в один месяц. Если уникальное изделие поступило на склад в одном месяце, а было отгружено через два месяца, то в таблице итогов останутся зафиксированными срезы за промежуточные месяцы. Даже когда текущий остаток обнулен, исторические записи об этом штрихкоде продолжают физически храниться в таблице итогов за прошлые периоды, увеличивая объем базы данных и фрагментируя индексы.
Главный фактор резкого падения скорости отгрузки при росте базы — некорректное получение остатков в запросе. Выясним причину этой проблемы. Если разработчик передает в виртуальную таблицу Остатки параметр даты (например, ТекущаяДата() или границу момента времени документа), платформа вынуждена восстанавливать остаток. Для этого СУБД соединяет ближайший рассчитанный исторический срез таблицы итогов с дельтой из таблицы движений.
При миллионах уникальных записей такая операция приводит к полному сканированию огромных таблиц (Index Scan или Table Scan), вызывая многосекундные зависания при каждом считывании штрихкода сканером. Чтобы запрос читал исключительно компактный актуальный срез без обхода движений, параметр «Период» передавать категорически нельзя.
Посмотрим на пример правильного запроса для проверки остатка конкретного изделия:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОстаткиТоваров.Номенклатура КАК Номенклатура,
| ОстаткиТоваров.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(
| ,
| Штрихкод = &ИскомыйШтрихкод) КАК ОстаткиТоваров";
Запрос.УстановитьПараметр("ИскомыйШтрихкод", ШтрихкодСоСканера);
РезультатЗапроса = Запрос.Выполнить();
Обратите внимание: первый параметр виртуальной таблицы оставлен пустым. Только в этом случае СУБД обращается напрямую к срезу актуальных итогов.
Нередко разработчики выбирают для измерения штрихкода тип Число, полагая, что числовые индексы всегда работают быстрее строковых. Рассмотрим, почему в случае формата Code 128 это приводит к ошибкам:
000458129). При записи в числовое поле нули автоматически отсекаются, в результате чего значение в регистре перестает совпадать со считанной строкой из сканера, требуя постоянных преобразований через Формат().Для хранения уникальных штрихкодов рекомендуется использовать тип Строка фиксированной длины, что гарантирует точное совпадение данных и сохранение ведущих нулей без снижения эффективности поиска по индексу.
Если система уже спроектирована с уникальным штрихкодом в измерении регистра накопления, стабильность ее работы зависит от регулярного сервисного обслуживания базы данных. Разберем по шагам необходимые операции:
Хранить единичные штрихкоды в регистре накопления в долгосрочной перспективе неэффективно, так как концепция регистров накопления создана для агрегации данных («множество движений дают мало итогов»). При поштучном учете агрегации нет: на один приход всегда приходится один расход. Рассмотрим более надежный архитектурный шаблон, который решает проблему кардинальности раз и навсегда.
Разделим контур учета на две составляющие:
Номенклатура и Склад. В таком регистре для 12–15 номенклатурных позиций таблица итогов будет содержать всего несколько десятков записей. Проведение документов и контроль остатков будут выполняться мгновенно.Штрихкод (тип Строка фиксированной длины).Номенклатура, Склад, Статус (значения перечисления: «На складе», «Отгружен»).Кроме того, в случае использования регистра сведений отгруженные штрихкоды можно безболезненно переносить регламентным заданием в архивную таблицу, сохраняя рабочий объем регистра минимальным, чего невозможно сделать в стандартном регистре накопления без удаления документов-регистраторов.