При разработке отчетов в конфигурации "Управление торговлей 10.3" программисты часто сталкиваются с проблемой медленной работы запросов, которые собирают данные по остаткам из разных регистров и одновременно пытаются получить актуальные цены. Ситуация осложняется, когда необходимо объединить данные по оптовым складам и розничным точкам. В этой статье мы подробно разберем, почему типовой запрос с использованием ЛЕВОЕ СОЕДИНЕНИЕ к виртуальным таблицам может "тормозить", и как переписать его по канонам высокой производительности 1С.
Рассмотрим типичный, но неоптимальный подход. Программист пытается в одном запросе объединить через ОБЪЕДИНИТЬ ВСЕ данные из РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты и РегистрНакопления.ТоварыВРознице.ОстаткиИОбороты, выполняя к каждому из них ЛЕВОЕ СОЕДИНЕНИЕ с виртуальной таблицей СрезПоследних регистра сведений цены. Основные причины низкой скорости здесь следующие:
(&НачалоПериода, &КонецПериода), система может пытаться обработать избыточный объем данных, даже если в консоли или СКД фильтры установлены.Первым делом проанализируем важность параметров виртуальных таблиц. Даже если вы используете СКД (Систему компоновки данных), рекомендуется явно указывать параметры в тексте запроса. Это гарантирует, что отборы будут применены на уровне СУБД еще до начала формирования временных выборок.
Посмотрим на пример правильного объявления параметров:
ВЫБРАТЬ
Товары.Номенклатура,
Товары.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, {(Склад).* КАК Склад, (Номенклатура).* КАК Номенклатура}) КАК Товары
Обратите внимание на использование фигурных скобок {} — это расширение языка запросов для СКД, которое позволяет гибко управлять отборами.
Чтобы избежать многократного и тяжелого обращения к таблице цен, мы применим методику пакетных запросов. Суть метода заключается в том, чтобы сначала собрать все нужные нам товары во временную таблицу, а затем одним махом получить цены только для этого узкого списка.
Разберем этот процесс по шагам. Сначала создадим временную таблицу ВТ_СписокТоваров:
ВЫБРАТЬ
Остатки.Склад КАК Склад,
Остатки.Номенклатура КАК Номенклатура,
Остатки.КоличествоНачальныйОстаток,
Остатки.КоличествоКонечныйОстаток,
Остатки.КоличествоПриход,
Остатки.КоличествоРасход
ПОМЕСТИТЬ ВТ_СписокТоваров
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, , , ) КАК Остатки
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ОстаткиРозница.Склад,
ОстаткиРозница.Номенклатура,
ОстаткиРозница.КоличествоНачальныйОстаток,
ОстаткиРозница.КоличествоКонечныйОстаток,
ОстаткиРозница.КоличествоПриход,
ОстаткиРозница.КоличествоРасход
ИЗ
РегистрНакопления.ТоварыВРознице.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, , , ) КАК ОстаткиРозница
;
////////////////////////////////////////////////////////////////////////////////
ИНДЕКСИРОВАТЬ ПО
Номенклатура
Важный момент: использование инструкции ИНДЕКСИРОВАТЬ ПО для временной таблицы критически важно. Это позволит последующему соединению с ценами работать мгновенно.
Теперь, когда у нас есть список только тех товаров, по которым есть остатки или движения, мы получим их цены. Мы будем использовать временную таблицу ВТ_СписокТоваров в качестве фильтра для виртуальной таблицы СрезПоследних.
ВЫБРАТЬ
Цены.Номенклатура,
Цены.ТипЦен,
Цены.Цена
ПОМЕСТИТЬ ВТ_Цены
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&КонецПериода, Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура ИЗ ВТ_СписокТоваров)) КАК Цены
ГДЕ
Цены.ТипЦен = &ТипЦен
;
////////////////////////////////////////////////////////////////////////////////
ИНДЕКСИРОВАТЬ ПО
Номенклатура
Проанализируем ситуацию: теперь вместо того, чтобы сканировать весь регистр цен или делать подзапросы для каждой строки склада, 1С выберет из базы данных только те записи, которые соответствуют товарам на складах. Это на порядки быстрее.
На последнем этапе мы соединяем наши временные таблицы. Также важно исправить логическую ошибку, часто встречающуюся в таких запросах: суммирование цены в итогах. Цена — это характеристика товара, ее сумма не имеет смысла. Для оценки "объема склада" необходимо умножать количество на цену.
ВЫБРАТЬ
Список.Склад КАК Склад,
Список.Номенклатура КАК Номенклатура,
Цены.ТипЦен КАК ТипЦен,
ЕСТЬNULL(Цены.Цена, 0) КАК Цена,
Список.КоличествоНачальныйОстаток КАК НачОст,
Список.КоличествоКонечныйОстаток КАК КонОст,
Список.КоличествоПриход КАК Приход,
Список.КоличествоРасход КАК Расход,
ЕСТЬNULL(Цены.Цена, 0) * Список.КоличествоКонечныйОстаток КАК СтоимостьОстатка
ИЗ
ВТ_СписокТоваров КАК Список
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цены КАК Цены
ПО Список.Номенклатура = Цены.Номенклатура
УПОРЯДОЧИТЬ ПО
Склад,
Номенклатура
ИТОГИ
СУММА(НачОст),
СУММА(КонОст),
СУММА(Приход),
СУММА(Расход),
СУММА(СтоимостьОстатка)
ПО
ОБЩИЕ,
Склад
Выясним еще несколько нюансов, которые могут повлиять на производительность в УТ 10.3:
ТоварыВРознице часто хранятся сразу в розничных ценах (ресурс СуммаПродажная). Если ваша цель — именно розничные цены, возможно, вам даже не нужно обращаться к регистру сведений ЦеныНоменклатуры, достаточно взять данные из регистра накопления.Применяя методику временных таблиц и индексации, мы переводим выполнение запроса из режима "построчного перебора" в режим эффективной массовой обработки данных. Это стандарт де-факто для разработки производительных систем на платформе 1С:Предприятие 8.