При разработке на платформе 1С часто возникает задача: рассчитать расход (или приход) за определённый период, причём сделать это только для заданных типов документов-регистраторов. Возникает закономерный вопрос: какой подход использовать? Обращаться напрямую к физической таблице движений или пытаться вытащить данные через виртуальную таблицу оборотов? Разберем эту ситуацию по шагам, проанализируем частые ошибки и выясним, какой вариант является наиболее оптимальным с точки зрения производительности и стандартов 1С.
Рассмотрим первоначальный подход, когда данные выбираются напрямую из физической таблицы регистра накопления. Например, нам нужны данные из регистра ТоварыНаСкладах.
Посмотрим на пример запроса:
ВЫБРАТЬ
ТоварыНаСкладах.Номенклатура КАК Номенклатура,
СУММА(ТоварыНаСкладах.ВНаличии) КАК Количество
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ГДЕ
ТоварыНаСкладах.Номенклатура В (&СписокНоменклатуры)
И ТоварыНаСкладах.Склад = &Склад
И ТоварыНаСкладах.Период МЕЖДУ &НачалоПериода И &КонецПериода
И ТоварыНаСкладах.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
И ТипЗначения(ТоварыНаскладах.Регистратор) В (&СписокТипов)
СГРУППИРОВАТЬ ПО
ТоварыНаСкладах.Номенклатура
Этот подход работает корректно и выполняет поставленную задачу. Мы явно указываем отбор по периоду, складу, списку номенклатуры, фильтруем по ВидДвиженияНакопления.Расход и ограничиваем типы регистраторов через функцию ТипЗначения. Однако у многих разработчиков возникает желание оптимизировать код и перевести его на использование виртуальной таблицы оборотов. Проанализируем, стоит ли это делать.
Если мы попытаемся решить задачу через виртуальную таблицу Обороты, нам необходимо указать периодичность Регистратор в параметрах виртуальной таблицы, туда же перенести отборы по измерениям (номенклатуре и складу), а в секции ГДЕ оставить условие по типу документа.
Рассмотрим пример кода, который часто получается в таких случаях:
ВЫБРАТЬ
ТоварыНаСкладахОбороты.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОбороты.ВНаличииРасход КАК Количество
ИЗ
РегистрНакопления.ТоварыНаСкладах.Обороты(&НачалоПериода, &КонецПериода, Регистратор, Склад = &Склад) КАК ТоварыНаСкладахОбороты
ГДЕ
ТипЗначения(ТоварыНаСкладахОбороты.Регистратор) В (&СписокТипов)
Частая ошибка: При выполнении такого запроса можно столкнуться с ситуацией, когда система возвращает непонятную единицу вместо фактического количества расхода. Выясним причину. Если в виртуальной таблице указана периодичность Регистратор, но в секции выборки не выбрано поле регистратора, и при этом отсутствует агрегатная функция СУММА (или группировка настроена некорректно), то результат может схлопнуться неправильно. В итоге вы получаете первую попавшуюся запись или количество строк, а не сумму ресурсов.
Разберем подробнее архитектурные особенности платформы 1С:Предприятие, чтобы понять, какой из запросов будет работать быстрее.
Период + Регистратор. При обращении к физической таблице с отбором по периоду и регистратору СУБД максимально эффективно использует этот индекс.Регистратор, платформа всё равно вынуждена обращаться к основной физической таблице движений. При этом на сервер СУБД накладываются дополнительные вычислительные затраты на построение виртуальной таблицы «на лету». Это делает запрос медленнее.Проанализируем условие ТипЗначения(Регистратор) В (&СписокТипов). Хотя оно рабочее, существуют и другие, иногда более оптимальные способы фильтрации:
ССЫЛКА (например, Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг). На уровне SQL-сервера это генерирует более простой код без необходимости обращаться к таблицам метаданных для определения числового идентификатора типа.ТипЗначения с передачей массива — это стандартный и хороший путь. Главное — передавать в параметр &СписокТипов массив именно типов значений (или пустых ссылок соответствующих типов документов). SQL-сервер обрабатывает такое условие по полю составного типа (_DocumentTRef) достаточно быстро.Регистратор. Это полностью исключит проверки типов в условии ГДЕ основного запроса к регистру.Подводя итог, можно с уверенностью сказать: для задачи расчета оборотов за период по конкретным типам документов первоначальное решение через физическую таблицу регистра с фильтром ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход) является самым грамотным и производительным.
Попытка переписать этот запрос на использование виртуальной таблицы с периодичностью по регистратору — это ложная оптимизация. Она не только усложняет чтение запроса, но и снижает скорость его выполнения, так как заставляет СУБД выполнять лишнюю работу по конструированию избыточной виртуальной выборки.