Почему в СКД левое соединение виртуальных таблиц фильтрует данные по периоду и как это исправить?

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

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

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

Разбор ситуации: конфликт ожиданий и реальности

Рассмотрим сценарий, с которым столкнулся автор темы. В запросе используются две временные таблицы:

  1. Взаиморасчеты (Оплаты) — здесь устанавливается жесткий фильтр по периоду (например, текущий месяц).
  2. Заказы покупателей — здесь данные должны выбираться за все время, чтобы подтянуться к оплатам.

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

Причина проблемы: Механизм автозаполнения параметров

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

Даже если в тексте запроса вы написали:


ИЗ
    РегистрНакопления.ЗаказыПокупателей.Обороты(, , Период, ) КАК ЗаказыПокупателейОбороты

СКД при генерации финального запроса к СУБД превратит это в:


ИЗ
    РегистрНакопления.ЗаказыПокупателей.Обороты(&НачалоПериода, &КонецПериода, Период, ) КАК ЗаказыПокупателейОбороты

В результате таблица заказов ограничивается тем же интервалом, что и оплаты. Если заказ был сделан раньше &НачалоПериода, он просто не попадет в выборку второй временной таблицы, и левое соединение не найдет соответствия.

Решение №1: Использование фигурных скобок (Расширение языка запросов)

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

Проанализируем исправленный вариант обращения к виртуальной таблице:


ВЫБРАТЬ
    ЗаказыПокупателейОбороты.ЗаказПокупателя,
    ...
ИЗ
    РегистрНакопления.ЗаказыПокупателей.Обороты({&НачалоПериодаЗаказов}, {&КонецПериодаЗаказов}, Период, ) КАК ЗаказыПокупателейОбороты

Используя конструкцию {&НачалоПериодаЗаказов}, мы создаем новые параметры, которые будут видны в настройках СКД отдельно от стандартных дат отчета. Если оставить эти параметры незаполненными в настройках, СКД не будет накладывать ограничение на период в этой таблице, и вы получите данные за всю историю.

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

Решение №2: Разделение наборов данных

Рассмотрим альтернативный архитектурный подход. Вместо одного сложного запроса с пакетом временных таблиц, можно создать два отдельных Набора данных - запрос:

  1. Первый набор: только оплаты за период.
  2. Второй набор: заказы (без фильтра по периоду или со своим фильтром).

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

Почему левое соединение превращается во внутреннее?

Проанализируем еще одну тонкость, упомянутую в обсуждении. Иногда программист правильно настроил периоды, но данные все равно пропадают. Это случается, если в настройках компоновки (на вкладке «Отбор») пользователь добавил условие на поле из правой таблицы (в нашем случае — Заказы).

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

Инструменты для диагностики: Консоль компоновки данных

Посмотрим на ситуацию со стороны отладки. Обычная «Консоль запросов» не покажет вам проблему, так как она выполняет запрос «как есть». Но СКД — это генератор запросов. Текст, который вы написали в конфигураторе, и текст, который реально уйдет в базу данных — это разные вещи.

Для выявления подобных проблем всегда используйте Консоль системы компоновки данных. С ее помощью можно увидеть «Исполняемый запрос». Именно там вы сразу заметите, что в виртуальную таблицу заказов СКД самовольно подставила значения &НачалоПериода и &КонецПериода, ограничив выборку.

Итоги и рекомендации

  1. Контролируйте автозаполнение: Помните, что СКД «помогает» вам, подставляя даты периода во все доступные виртуальные таблицы оборотов и остатков.
  2. Используйте расширение языка: Фигурные скобки {} — ваш главный инструмент для управления тем, как СКД модифицирует запрос.
  3. Проверяйте роли полей: На вкладке «Наборы данных» убедитесь, что поля периодов имеют правильные роли. Если у обеих таблиц стоит роль «Период», СКД может пытаться связать их по датам более жестко, чем вам нужно.
  4. Связи наборов данных: Для сложных отчетов, где разные части данных требуют разных временных рамок, разделение запросов на несколько наборов данных часто является более чистым и надежным решением.

Применяя эти методы, вы сможете создавать гибкие отчеты, которые корректно обрабатывают данные вне зависимости от того, в какие периоды времени происходили связанные события.

← На главную