Почему количество строк в консоли запросов и отчете СКД не совпадает?

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

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

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

Разбор ситуации: программное формирование набора данных

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

Типичный пример кода для такой задачи:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ ... ИЗ ... ГДЕ Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";
    
    // Установка параметров
    Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
    Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
    
    ТЗ = Запрос.Выполнить().Выгрузить();
    
    ВнешниеНаборыДанных = Новый Структура;
    ВнешниеНаборыДанных.Вставить("ТЗ", ТЗ);
    
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

Казалось бы, если запрос в консоли и в модуле идентичен, результат должен совпадать. Однако именно здесь кроется первая и самая распространенная ловушка.

Причина 1: Несоответствие параметров дат

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

Выясним причину расхождения: СКД использует выражения параметров только тогда, когда она сама выполняет запрос. Если же мы формируем Запрос в модуле вручную, то параметры, установленные в компоновщике настроек, могут не соответствовать тем, что мы передаем в объект Запрос. Например, если пользователь выбрал дату «11.05.2024», то без явного указания КонецДня() в коде, запрос получит «11.05.2024 00:00:00», и все документы за этот день (кроме созданных ровно в полночь) не попадут в выборку.

Решение проблемы: Всегда проверяйте значения параметров в отладчике непосредственно перед методом Запрос.Выполнить(). Для корректного захвата данных за весь день используйте функцию КонецДня():

Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаОкончания));

Причина 2: Интеллектуальная оптимизация запроса СКД

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

Если пользователь в настройках отчета (в выбранных полях) не указал ни одного поля из этой присоединенной таблицы, СКД просто удалит это соединение из финального SQL-запроса, чтобы повысить производительность.

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

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

Причина 3: Влияние RLS (Ограничение доступа на уровне записей)

Это одна из самых «невидимых» причин. Консоль запросов часто запускается под правами администратора, где RLS не действует. Отчет же выполняется под правами текущего пользователя.

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

Проверим гипотезу: Запустите отчет под пользователем с полными правами. Если данные совпали с консолью — проблема в настройках RLS.

Причина 4: Виртуальные таблицы и объект «Граница»

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

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

ДобавитьКДате(КонецПериода(&ДатаОкончания, "День"), "Секунда", 1)

Или программно создавать объект Граница с видом ВидГраницы.Включая. Разница в подходе к определению момента времени часто дает расхождение в несколько строк или в итоговых суммах.

Причина 5: Автозаполнение и временные таблицы

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

Рассмотрим рекомендации опытных разработчиков:

  1. Попробуйте отключить «Автозаполнение» и прописать все инструкции {ГДЕ...} и {ВЫБРАТЬ...} вручную в тексте запроса.
  2. Если используете временные таблицы, старайтесь избегать вложенных запросов там, где это возможно, так как СКД сложнее их анализировать.

Как гарантированно найти причину?

Чтобы не гадать, почему данные расходятся, нужно проанализировать реальный текст запроса, который СКД отправляет на сервер СУБД. Сделать это можно двумя способами:

  1. Через отладчик: В процедуре ПриКомпоновкеРезультата после выполнения метода КомпоновщикМакета.Выполнить() посмотрите значение МакетКомпоновки.НаборыДанных.НаборДанных1.Запрос. Там будет текст запроса с уже подставленными параметрами и условиями.
  2. С помощью Консоли компоновки данных: Это специальный инструмент (часто входит в состав инструментов разработчика), который позволяет увидеть финальный запрос. Сравните его с тем, что вы запускаете в обычной консоли запросов.

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

← На главную