В практике разработки отчетов на Системе компоновки данных (СКД) в 1С часто встречается парадоксальная ситуация. Мы запускаем отчет с отбором только по периоду — и нужная нам запись (например, со значением "ААА" в определенном поле) упорно не выводится. Но стоит нам установить в настройках явный отбор по этому значению (Поле 1 = "ААА"), как отчет мгновенно выводит именно эту строку. На первый взгляд это кажется мистикой: как условие, сужающее выборку, может «добавлять» в нее новые данные?
В этой статье мы подробно разберем, почему СКД так кардинально меняет логику работы запросов, какие внутренние механизмы платформы за это отвечают и по какому алгоритму нужно действовать разработчику, чтобы локализовать и исправить эту ошибку.
Главное, что нужно понимать при работе с СКД: компоновщик макета никогда не выполняет ваш исходный запрос «как есть». Перед отправкой запроса СУБД платформа выполняет глубокий анализ настроек отчета (выбранных полей, группировок, параметров и отборов) и динамически перестраивает (оптимизирует) весь пакет запросов. Когда мы накладываем отбор по конкретному полю, запускаются сложные оптимизационные алгоритмы, которые могут непреднамеренно сломать логику разработчика.
Проанализируем основные причины такого поведения:
1. Механизм «проталкивания» отборов (Filter Propagation)
СКД стремится наложить отборы как можно раньше, чтобы СУБД обрабатывала минимальный объем данных. Если у нас сложный пакетный запрос (например, состоящий из 8 временных таблиц), то:
МАКСИМУМ, СУММА) или условий ГДЕ запись "ААА" отсекается, мы не увидим её в итоговом результате."ААА" успешно доходит до финала.2. Динамическое превращение ЛЕВОГО соединения во ВНУТРЕННЕЕ
Это классическая архитектурная ошибка при проектировании запросов для СКД. Предположим, у нас есть левое соединение с таблицей, откуда мы берем наше Поле 1. Если мы накладываем отбор по этому полю, СКД может автоматически добавить условие фильтрации в секцию ГДЕ итогового запроса. В реляционных базах данных любое сравнение значения из левой таблицы с конкретным значением (кроме проверки на NULL) автоматически превращает соединение во внутреннее:
// Было (упрощенно без отбора):
ВЫБРАТЬ ОсновныеДанные.Ссылка, Свойства.Значение КАК Поле1
ИЗ Справочник.Номенклатура КАК ОсновныеДанные
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойств КАК Свойства
ПО ОсновныеДанные.Ссылка = Свойства.Объект
// Стало (при наложении отбора в СКД):
ВЫБРАТЬ ОсновныеДанные.Ссылка, Свойства.Значение КАК Поле1
ИЗ Справочник.Номенклатура КАК ОсновныеДанные
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойств КАК Свойства
ПО ОсновныеДанные.Ссылка = Свойства.Объект
ГДЕ Свойства.Значение = &П_Поле1 // Соединение фактически стало ВНУТРЕННИМ!
В результате без отбора часть записей могла теряться из-за других условий, а при наложении отбора логика связей перестраивается, выдавая неожиданный точечный результат.
3. Влияние механизма автозаполнения (Auto-fill)
Если в настройках схемы включено автозаполнение, СКД самостоятельно решает, какие таблицы и соединения нужны для построения отчета:
Поле 1 не выбрано в структуре вывода и по нему нет отбора, СКД может вообще выбросить из результирующего SQL-запроса «лишние» (по её мнению) временные таблицы или левые соединения.4. Специфика обработки объединений (UNION)
Если в запросе используются конструкции ОБЪЕДИНИТЬ или ОБЪЕДИНИТЬ ВСЕ, СКД накладывает отборы весьма специфически. Отбор может быть наложен не на весь результат объединения, а только на одну из его веток, что приводит к искажению промежуточных данных во временных таблицах.
---
Единственный надежный способ победить эту «мистику» — увидеть своими глазами, во что именно СКД превращает ваш исходный запрос в обоих режимах работы. Рассмотрим детальный алгоритм диагностики.
Для анализа нам понадобится Консоль компоновки данных (ее можно скачать на ИТС). Обычная консоль запросов здесь не поможет, так как она не умеет эмулировать поведение компоновщика макета с учетом пользовательских настроек.
Поле 1 = "ААА". Снова сформируйте отчет и зафиксируйте полученный текст запроса.Откройте оба полученных текста запросов в любой программе сравнения текстов (например, встроенное сравнение файлов в 1С, Notepad++ с плагином Compare или KDiff3) и проанализируйте следующие моменты:
ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) из-за некорректного размещения условий ГДЕ.---
Выяснив причину изменения логики запроса, мы можем применить один из следующих инструментов управления СКД:
Если автозаполнение СКД некорректно «проталкивает» отбор во вложенные временные таблицы, нарушая их логику, мы можем отключить автозаполнение для этого поля и прописать условия вручную с помощью синтаксиса фигурных скобок {}:
// В нужном запросе пакета, где отбор гарантированно безопасен:
ВЫБРАТЬ
Продажи.Контрагент,
Продажи.СуммаОборот
ИЗ
РегистрНакопления.Продажи.Обороты КАК Продажи
{ГДЕ
Продажи.Контрагент.* КАК Контрагент}
Этот синтаксис указывает СКД, что отбор по контрагенту должен накладываться строго в этом месте и нигде более.
Если СКД без наложенного отбора выкидывает важные таблицы из запроса, перейдите в конструктор запроса на вкладку Компоновка данных. Нажмите на нужную таблицу правой кнопкой мыши и установите для неё флаг Обязательная. Это жестко укажет системе компоновки, что данную таблицу и связи с ней нельзя исключать из итогового SQL-запроса, даже если её поля не выбраны пользователем.
Если вы делаете левое соединение и накладываете отбор по полю из правой (присоединяемой) таблицы, обязательно обрабатывайте возможные значения NULL, чтобы левое соединение не превращалось во внутреннее. Также проверяйте, чтобы отборы по полям правой таблицы накладывались в условиях связи (секция ПО), а не в секции ГДЕ.
Используя этот системный подход и детальное сравнение запросов в консоли СКД, вы навсегда избавитесь от «мистики» с исчезающими записями в ваших отчетах!