При оптимизации и очистке справочников в 1С (в частности, в конфигурации «Управление производственным предприятием») часто возникает задача: найти все позиции номенклатуры, по которым в текущий момент отсутствуют остатки на складах и счетах бухгалтерского учета, и установить на них пометку на удаление. Разберем по шагам, как правильно составить запрос к базе данных и какие архитектурные нюансы необходимо учесть при реализации такой обработки.
Частой ошибкой начинающих разработчиков является попытка выбрать список номенклатуры напрямую из регистра остатков (например, РегистрНакопления.ТоварыНаСкладах.Остатки). В виртуальных таблицах остатков содержатся только те позиции, по которым есть ненулевой количественный или суммовой остаток на указанный момент времени. Если остаток товара равен нулю или движений по нему никогда не было, его просто не окажется в таблице остатков.
Поэтому исходной базой для выборки всегда должен выступать справочник Справочник.Номенклатура, к которому присоединяются данные из регистров остатков. Также важно учесть следующие архитектурные особенности конфигурации:
ПартииТоваровНаСкладах) не заполняются. В этом случае остатки себестоимости хранятся в регистрах УчетЗатрат и УчетЗатратРегл.Рассмотрим базовый вариант запроса, который позволяет получить элементы справочника, отсутствующие в виртуальной таблице остатков склада. Этот способ подходит для небольших баз данных и оперативной проверки.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
НЕ Номенклатура.ЭтоГруппа
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура.Предопределенный
И НЕ Номенклатура.Услуга
И НЕ Номенклатура.Ссылка В
(ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК Остатки
ГДЕ
Остатки.КоличествоОстаток > 0)
Важно: Конструкция ГДЕ НЕ Ссылка В (ВЫБРАТЬ ...) при работе с большими объемами данных (десятки и сотни тысяч позиций) создает существенную нагрузку на СУБД, так как подзапрос может приводить к неоптимальному плану выполнения.
Проанализируем более надежный и масштабируемый вариант. Мы соберем во временную таблицу все ненулевые остатки из складских регистров и плана счетов бухгалтерского учета, после чего свяжем ее со справочником через ЛЕВОЕ СОЕДИНЕНИЕ.
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_Остатки
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК ТоварыНаСкладахОстатки
ГДЕ
ТоварыНаСкладахОстатки.КоличествоОстаток > 0
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ХозрасчетныйОстатки.Субконто1 КАК Номенклатура
ИЗ
РегистрБухгалтерии.Хозрасчетный.Остатки(
&Период,
Счет В ИЕРАРХИИ (&СчетаТМЦ),
&ВидыСубконто,
) КАК ХозрасчетныйОстатки
ГДЕ
ХозрасчетныйОстатки.КоличествоОстаток > 0
ИЛИ ХозрасчетныйОстатки.СуммаОстаток > 0
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТ_Остатки.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_УникальныеОстатки
ИЗ
ВТ_Остатки КАК ВТ_Остатки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Номенклатура.Ссылка КАК НоменклатураСсылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_УникальныеОстатки КАК ВТ_УникальныеОстатки
ПО Номенклатура.Ссылка = ВТ_УникальныеОстатки.Номенклатура
ГДЕ
ВТ_УникальныеОстатки.Номенклатура ЕСТЬ NULL
И НЕ Номенклатура.ЭтоГруппа
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура.Предопределенный
И НЕ Номенклатура.Услуга
Посмотрим, как безопасно выполнить установку пометки на удаление в цикле. При массовой обработке данных рекомендуется выполнять запись порциями в транзакциях, чтобы не вызывать длительных блокировок таблиц базы данных.
Также важно помнить о контроле ссылочной целостности: если товар участвовал в движениях прошлых периодов, стандартная процедура удаления помеченных объектов не позволит физически удалить его из базы, пока на него ссылаются проведенные документы.
Запрос = Новый Запрос;
// Устанавливаем текст запроса из Варианта 2
Запрос.Текст = ПолучитьТекстЗапроса();
Запрос.УстановитьПараметр("Период", ТекущаяДата());
СчетаТМЦ = Новый Массив;
СчетаТМЦ.Добавить(ПланыСчетов.Хозрасчетный.Материалы);
СчетаТМЦ.Добавить(ПланыСчетов.Хозрасчетный.Товары);
СчетаТМЦ.Добавить(ПланыСчетов.Хозрасчетный.ГотоваяПродукция);
Запрос.УстановитьПараметр("СчетаТМЦ", СчетаТМЦ);
ВидыСубконто = Новый Массив;
ВидыСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Номенклатура);
Запрос.УстановитьПараметр("ВидыСубконто", ВидыСубконто);
Выборка = Запрос.Выполнить().Выбрать();
Счетчик = 0;
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
Попытка
ОбъектНоменклатура = Выборка.НоменклатураСсылка.ПолучитьОбъект();
Если ОбъектНоменклатура <> Неопределено Тогда
ОбъектНоменклатура.УстановитьПометкуУдаления(Истина);
Счетчик = Счетчик + 1;
КонецЕсли;
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("ПометкаНоменклатуры", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
Возврат;
КонецПопытки;
// Фиксируем порции по 500 элементов
Если Счетчик % 500 = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
Если Счетчик % 500 <> 0 Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
Если требуется скрыть неиспользуемую номенклатуру от пользователей, но сохранить возможность построения отчетов за прошлые периоды без конфликтов контроля ссылок, альтернативным решением является перенос найденных позиций в отдельную группу (например, «_Архив») с ограничением видимости в формах выбора.