Работа с регистром бухгалтерии в платформе 1С:Предприятие требует от разработчика строгого соблюдения стандартов кодирования. Неоптимально написанный запрос к виртуальной таблице ДвиженияССубконто на реальной базе данных объемом в несколько гигабайт может запросто привести к зависанию сервера СУБД или аварийному завершению работы сеанса пользователя с ошибкой нехватки памяти (Out of Memory). В этой статье мы подробно разберем практический кейс, рассмотрим типичные ошибки при получении данных по отгруженной возвратной таре (дебет счета 73.03) и построим эталонный быстрый запрос.
Рассмотрим ситуацию: разработчику необходимо получить данные по отгруженной возвратной таре из документов «Реализация товаров и услуг» (РТУ), по которым в учете были сформированы проводки по дебету счета 73.03 (в корреспонденции со счетом 41.03).
Первая попытка автора темы заключалась в левом соединении табличной части документа РеализацияТоваровУслуг.ВозвратнаяТара с виртуальной таблицей РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто по полю Регистратор с последующим наложением фильтра по счету дебета в секции ГДЕ. Этот подход привел к тому, что в выборку попала абсолютно вся тара из документов, даже та, по которой не было соответствующих проводок. Попытка исправить ситуацию и вытащить субконто привела к полному зависанию системы.
Выясним причины такого поведения платформы 1С и СУБД:
ГДЕ. Это классическая причина зависаний.ГДЕ ХозрасчетныйДвиженияССубконто.СчетДт = &Счет заставляет базу данных выполнять избыточную работу, считывая лишние данные по всем остальным счетам, прежде чем отсечь их на финальном этапе выполнения запроса.В ходе обсуждения на форуме автор пришел к промежуточному решению, которое "заработало" на небольшом объеме данных:
ВЫБРАТЬ
ХозрасчетныйДвиженияССубконто.Регистратор КАК Регистратор,
ХозрасчетныйДвиженияССубконто.СчетДт,
ХозрасчетныйДвиженияССубконто.СчетКт,
ХозрасчетныйДвиженияССубконто.СубконтоКт1
ИЗ
РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(&ДатаНачало, &ДатаОкончание, СубконтоДт1 В ИЕРАРХИИ (&Экспедитор), , ) КАК ХозрасчетныйДвиженияССубконто
ГДЕ
ХозрасчетныйДвиженияССубконто.СчетДт = &Счет
И ВЫРАЗИТЬ(ХозрасчетныйДвиженияССубконто.Регистратор.Контрагент КАК Справочник.Контрагенты) В ИЕРАРХИИ (&Контрагент)
Хотя этот запрос вернул результат, он содержит три критические архитектурные ошибки, которые обязательно "выстрелят" при увеличении базы данных или при запуске в многопользовательском режиме.
Поле Регистратор в регистре бухгалтерии имеет составной тип. Оно может ссылаться на десятки различных видов документов (Реализация, Отчет о розничных продажах, ОперацияБух и т.д.). Конструкция ХозрасчетныйДвиженияССубконто.Регистратор.Контрагент заставляет платформу на уровне СУБД генерировать неявные левые соединения (LEFT JOIN) со всеми таблицами документов, у которых физически может быть реквизит Контрагент. Функция ВЫРАЗИТЬ для результата не спасает от этих соединений. В итоге SQL-запрос превращается в огромную "простыню" соединений, нагружающую процессор сервера СУБД.
Условие по счету дебета по-прежнему находится в секции ГДЕ. СУБД вынуждена анализировать все проводки за период по экспедитору, а не только проводки по счету 73.03.
Виртуальная таблица ДвиженияССубконто выбирает в том числе неактивные движения (например, движения по распроведенным документам или помеченным на удаление, если они физически остались в базе). Без явного указания фильтра по активности отчет может вывести некорректные "фантомные" данные.
Для решения этой задачи применим самый надежный и быстрый паттерн проектирования запросов в 1С — пакетный запрос с использованием индексированных временных таблиц.
Разобьем решение на два последовательных шага:
Шаг 1. Выборка документов. Сначала мы обратимся к физической таблице документов Документ.РеализацияТоваровУслуг, быстро отфильтруем нужные нам документы по периоду, контрагенту и экспедитору, после чего поместим их ссылки во временную таблицу и проиндексируем ее.
Шаг 2. Выборка из регистра бухгалтерии. Во втором запросе пакета мы обратимся к виртуальной таблице регистра бухгалтерии, передав все необходимые фильтры (включая массив отобранных документов и конкретный счет дебета) непосредственно в параметры этой виртуальной таблицы.
Посмотрим на пример реализации такого оптимального запроса:
// ШАГ 1: Отбираем только целевые документы во временную таблицу
ВЫБРАТЬ
Реализация.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Документы
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Дата МЕЖДУ &ДатаНачало И &ДатаОкончание
И Реализация.Контрагент В ИЕРАРХИИ(&Контрагент)
И Реализация.Экспедитор В ИЕРАРХИИ(&Экспедитор)
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
////////////////////////////////////////////////////////////////////////////////
// ШАГ 2: Извлекаем проводки только по отобранным документам
ВЫБРАТЬ
Движения.Регистратор КАК Регистратор,
Движения.СчетДт КАК СчетДт,
Движения.СчетКт КАК СчетКт,
Движения.СубконтоКт1 КАК НоменклатураТара,
Движения.КоличествоДт КАК КоличествоДт,
Движения.КоличествоКт КАК КоличествоКт
ИЗ
РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(
&ДатаНачало,
&ДатаОкончание,
СчетДт = &Счет
И Регистратор В (ВЫБРАТЬ ВТ_Документы.Ссылка ИЗ ВТ_Документы)
И Активность,
,
) КАК Движения
Проанализируем преимущества предложенной схемы:
Регистратор.Контрагент. Фильтрация по реквизитам шапки документа происходит на уровне первого запроса пакета, где мы работаем с явной физической таблицей Документ.РеализацияТоваровУслуг. На уровне СУБД это выполняется мгновенно с использованием штатных индексов таблицы документов.ИНДЕКСИРОВАТЬ ПО Ссылка подготавливает данные во временной таблице ВТ_Документы для быстрого соединения на уровне СУБД.ДвиженияССубконто. Это гарантирует, что СУБД выберет из базы данных исключительно те записи, которые нам нужны, не тратя ресурсы на чтение лишней информации.Используя этот подход, вы обеспечите стабильную и быструю работу системы даже при формировании отчетов за длительные периоды времени на крупных информационных базах.