Почему виртуальный регистр «ПродажиОбороты» в СКД считает неверно по сравнению с обычным запросом?

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

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

Причина 1: Автоматическая трансформация запроса в СКД

Главное, что нам нужно понять: СКД — это не просто «пускатель» запроса. Это мощный генератор, который переписывает ваш текст запроса перед отправкой на SQL-сервер. Когда мы пишем запрос в наборе данных, СКД анализирует выбранные поля, настройки группировок и отборы пользователя, а затем оптимизирует код под эти нужды.

Если в обычном запросе параметры виртуальных таблиц (&НачалоПериода, &КонецПериода) передаются жестко, то СКД может игнорировать их, подставляя свои значения из стандартного периода. Рассмотрим пример из сообщения автора:


ВЫБРАТЬ
    ПродажиОбороты.КоличествоОборот,
    ПродажиОбороты.Номенклатура,
    ПродажиОбороты.Период
ИЗ
    РегистрНакопления.Продажи.Обороты(НАЧАЛОПЕРИОДА(&НачалоПериода, МЕСЯЦ), КОНЕЦПЕРИОДА(&КонецПериода, МЕСЯЦ), Месяц, ) КАК ПродажиОбороты

Проанализируем ситуацию: если в настройках СКД пользователь установит другой период или наложит отбор по конкретной номенклатуре, компоновщик может вынести эти отборы внутрь виртуальной таблицы. Если связи между временными таблицами настроены неверно, это приведет к потере или дублированию данных.

Причина 2: «Раздувание» итогов при соединениях

В тексте темы мы видим левое соединение таблицы продаж за месяц с таблицей остатков по дням. Это классическая «ловушка» для ресурсов СКД. Давайте посмотрим на это соединение:


ИЗ ПродажиТоваров КАК ПродажиТоваров
ЛЕВОЕ СОЕДИНЕНИЕ ИтоговыеОстатки КАК ИтоговыеОстатки
ПО ПродажиТоваров.Номенклатура = ИтоговыеОстатки.Номенклатура
И (ПродажиТоваров.Период = НАЧАЛОПЕРИОДА(ИтоговыеОстатки.Период, МЕСЯЦ))

Разберем, что происходит: на одну запись о продажах за месяц (например, январь) приходится 31 запись из таблицы остатков (на каждый день января). Когда СКД начинает рассчитывать ресурс СУММА(КоличествоОборот), она видит в результирующей выборке 31 строку. В итоге сумма продаж будет завышена в 31 раз! Чтобы избежать этого, в настройках ресурсов СКД для полей, которые могут дублироваться из-за соединений «один ко многим», следует использовать функции МАКСИМУМ или СРЕДНЕЕ вместо простого суммирования, либо правильно настраивать роли полей.

Причина 3: Разыменование полей (обращение через точку)

В сообщении №5 форума было сделано важное замечание: «сплошное разыменование — тебя админ убьет за такой запрос». Посмотрим на этот фрагмент:


СГРУППИРОВАТЬ ПО
    Дни.ДатаКалендаря,
    ТоварыНаСкладахОстаткиИОбороты.Номенклатура,
    ТоварыНаСкладахОстаткиИОбороты.Номенклатура.Артикул

Обращение Номенклатура.Артикул внутри запроса (особенно внутри виртуальных таблиц или группировок) заставляет систему выполнять неявное левое соединение с таблицей справочника. Если таких обращений много, SQL-запрос превращается в «монстра» из десятков соединений. В СКД это усугубляется тем, что система сама добавляет соединения для получения представлений объектов. Рекомендация: всегда выбирайте только ссылку (Номенклатура), а дополнительные поля (Артикул, Производитель) выводите уже в настройках выбранных полей СКД — система сама их эффективно подтянет.

Как найти ошибку: Использование консоли компоновки данных

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

  1. Откройте отчет в консоли компоновки.
  2. Сформируйте настройки, при которых данные считаются неверно.
  3. Найдите вкладку или кнопку «Исполняемый запрос».
  4. Скопируйте этот текст (это будет чистый SQL-подобный текст запроса 1С со всеми подставленными параметрами и временными таблицами).
  5. Вставьте этот текст в обычную консоль запросов и выполните его.

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

Правильная работа с остатками и оборотами в СКД

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

Проанализируем шаги по исправлению структуры:

  1. Избавьтесь от вложенных запросов: Как сказано в сообщении №3, лучше создать две плоские временные таблицы (одна для остатков, другая для оборотов) и объединить их в третьей.
  2. Настройте роли полей: Для поля Период укажите роль «Период», для остатка — роль «Остаток» (с указанием типа: Начальный или Конечный).
  3. Используйте фигурные скобки: Чтобы СКД не перехватывала параметры виртуальных таблиц там, где не нужно, используйте синтаксис {&НачалоПериода}. Это жестко закрепит ваше условие.

Посмотрим, как можно оптимизировать получение данных по остаткам без лишних ВЫБОР КОГДА:


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

Такой подход позволит СКД корректно интерпретировать данные. Помните, что виртуальные таблицы — это не физические таблицы на диске, а динамические выборки, которые СУБД строит «на лету» (сообщения 9-11). Чем проще и прозрачнее будет ваш исходный запрос, тем меньше вероятность, что компоновщик при трансформации допустит логическую ошибку.

← На главную