Как исправить запрос СКД в УТ 11 при объединении продаж, закупок и остатков

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке сложных отчетов в 1С:Управление торговлей 11 программисты часто сталкиваются с ситуацией, когда по отдельности части запроса работают быстро и правильно, но при попытке собрать их в единую таблицу результат превращается в «хаос»: данные дублируются, появляются пустые значения (NULL), а время выполнения возрастает до неприемлемых значений. Рассмотрим подробно, почему это происходит и как правильно построить такой запрос на базе регистров себестоимости и выручки.

Анализ основной ошибки: Декартово произведение

Проанализируем фрагмент кода, представленный в исходном вопросе. В финальном блоке выбора данных таблица КонечныйОстаток добавлена через запятую без ключевого слова ПО и условий связи:


ИЗ
    Продажи КАК Продажи
        ПОЛНОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
        ПО Продажи.Номенклатура = Остатки.Номенклатура
        ПОЛНОЕ СОЕДИНЕНИЕ Закупки КАК Закупки
        ПО Продажи.Номенклатура = Закупки.Номенклатура,
    КонечныйОстаток КАК КонечныйОстаток

В языке запросов 1С перечисление таблиц через запятую означает Cross Join (декартово произведение). Это приводит к тому, что каждая строка из результата первых трех соединений умножается на каждую строку из таблицы КонечныйОстаток. Если в первой части 100 строк и во второй 100, на выходе получится 10 000 строк. Именно это является главной причиной «полного хаоса» в данных и критического замедления отчета даже при выборе периода в один день.

Проблема использования ПОЛНОГО СОЕДИНЕНИЯ для трех и более таблиц

Разберем ситуацию с логикой ПОЛНОЕ СОЕДИНЕНИЕ. Когда мы соединяем таблицу Продажи с таблицей Закупки по полю Номенклатура, мы ожидаем увидеть данные по всем товарам. Однако, если товар был закуплен, но не продан, в таблице Продажи значение номенклатуры будет NULL. При попытке присоединить третью таблицу (например, Остатки) по условию Продажи.Номенклатура = Остатки.Номенклатура, товары, которых нет в продажах, не соединятся корректно, так как NULL не равен значению.

Для решения этой проблемы при использовании соединений необходимо использовать конструкцию ЕСТЬNULL для полей связи:

ПО ЕСТЬNULL(Продажи.Номенклатура, Закупки.Номенклатура) = Остатки.Номенклатура

Однако такой подход делает запрос громоздким и трудночитаемым. Рассмотрим более эффективные методы.

Решение №1: Использование ОБЪЕДИНЕНИЕ ВСЕ (Рекомендуется)

Вместо тяжелых соединений в 1С предпочтительнее использовать конструкцию ОБЪЕДИНЕНИЕ ВСЕ в подзапросе или временной таблице. Это позволяет собрать все движения и остатки в один «столбец», а затем сгруппировать их по номенклатуре. Выясним, как это выглядит на практике:

  1. Создаем временную таблицу, в которую выбираем данные из всех источников по очереди.
  2. Для каждого источника заполняем только его целевые колонки, оставляя остальные нулевыми.
  3. Группируем результат по номенклатуре.

Пример структуры такого запроса:


ВЫБРАТЬ
    Т.Номенклатура КАК Номенклатура,
    СУММА(Т.КоличествоПродаж) КАК КоличествоПродаж,
    СУММА(Т.КоличествоЗакупок) КАК КоличествоЗакупок,
    СУММА(Т.НачальныйОстаток) КАК НачальныйОстаток
ПОМЕСТИТЬ ОбщиеДанные
ИЗ (
    ВЫБРАТЬ Номенклатура, КоличествоОборот КАК КоличествоПродаж, 0 КАК КоличествоЗакупок, 0 КАК НачальныйОстаток
    ИЗ РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(...)
    
    ОБЪЕДИНЕНИЕ ВСЕ
    
    ВЫБРАТЬ Номенклатура, 0, КоличествоПриход, 0
    ИЗ РегистрНакопления.СебестоимостьТоваров.Обороты(...)
    
    ОБЪЕДИНЕНИЕ ВСЕ
    
    ВЫБРАТЬ Номенклатура, 0, 0, КоличествоНачальныйОстаток
    ИЗ РегистрНакопления.СебестоимостьТоваров.ОстаткиИОбороты(...)
) КАК Т
СГРУППИРОВАТЬ ПО Т.Номенклатура

Решение №2: Использование виртуальной таблицы «Остатки и обороты»

Проанализируем возможности регистра СебестоимостьТоваров. Вместо того чтобы отдельно обращаться к таблицам Остатки и Обороты, в 1С предусмотрена виртуальная таблица ОстаткиИОбороты. Она позволяет получить начальный остаток, приход, расход и конечный остаток за один вызов.

Важный момент: В УТ 11 данные о себестоимости хранятся в разрезе ключей аналитики. Посмотрим на правильное обращение к данным:

  1. Обращаемся к РегистрНакопления.СебестоимостьТоваров.ОстаткиИОбороты.
  2. Используем поле АналитикаУчетаНоменклатуры.Номенклатура для группировки.
  3. Для получения данных о продажах с НДС и без НДС лучше использовать РегистрНакопления.ВыручкаИСебестоимостьПродаж, так как там данные уже агрегированы по документам продажи.

Оптимизация работы с НДС и суммами в УТ 11

В исходном запросе автора производится сложный расчет НДС через ВЫБОР КОГДА. Рассмотрим, почему это неэффективно:

Совет: Всегда используйте готовые ресурсы регистров, если они предусмотрены конфигурацией.

Настройка СКД для корректного расчета остатков

Если вы используете систему компоновки данных, лучшим решением будет не пытаться объединить всё в одном запросе, а использовать возможности Связей наборов данных:

  1. Создайте первый набор данных (Запрос) для получения остатков и закупок из регистра СебестоимостьТоваров.
  2. Создайте второй набор данных (Запрос) для получения продаж из регистра ВыручкаИСебестоимостьПродаж.
  3. На вкладке Связи наборов данных свяжите их по полю Номенклатура.
  4. В поле Параметры для связи укажите период, чтобы данные о продажах соответствовали периоду остатков.

Это позволит СКД автоматически обработать ситуацию, когда по товару есть остатки, но нет продаж (и наоборот), без использования нестабильных полных соединений.

Подведем итог: Чтобы отчет работал быстро и выдавал верные данные, необходимо избавиться от запятых в секции ИЗ (декартовых произведений), заменить ПОЛНОЕ СОЕДИНЕНИЕ на структуру ОБЪЕДИНЕНИЕ ВСЕ и максимально использовать готовые ресурсы конфигурации (такие как суммы без НДС), заложенные разработчиками 1С.

← На главную