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