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