Почему дублируются документы при объединении запросов в СКД и как это исправить?

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

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

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

Выясняем причину: почему классические методы SQL не помогают в СКД?

Рассмотрим стандартную ситуацию. Нам необходимо собрать документы из двух источников: табличной части «История» документа ВзаимодействияСКлиентом и регистра сведений ВерсииОбъектов. Автор темы на форуме попытался объединить их следующим запросом:


ВЫБРАТЬ
    ВзаимодействияСКлиентомИстория.Ссылка КАК Документ
ПОМЕСТИТЬ Взаимод
ИЗ
    Документ.ВзаимодействияСКлиентом.История КАК ВзаимодействияСКлиентомИстория
ГДЕ
    НАЧАЛОПЕРИОДА(ВзаимодействияСКлиентомИстория.Дата, ДЕНЬ) МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
СГРУППИРОВАТЬ ПО
    ВзаимодействияСКлиентомИстория.Ссылка ;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВерсииОбъектов.Объект ПОМЕСТИТЬ Версии
ИЗ
    РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
    НАЧАЛОПЕРИОДА(ВерсииОбъектов.ДатаВерсии, ДЕНЬ) МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
    И ВерсииОбъектов.Объект ССЫЛКА Документ.ВзаимодействияСКлиентом ;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Версии.Объект КАК Документ
ИЗ
    Версии КАК Версии
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    Взаимод.Документ ИЗ Взаимод КАК Взаимод

При выводе этого отчета один и тот же документ выводится дважды. Даже замена конструкций или добавление ВЫБРАТЬ РАЗЛИЧНЫЕ в финальном пакете запроса не решают проблему до конца. На это есть три скрытые причины, связанные с внутренней логикой работы СКД.

  1. Влияние механизма автозаполнения СКД. По умолчанию в наборе данных типа «Запрос» включен флаг «Автозаполнение». СКД анализирует схему и автоматически модифицирует исходный SQL-запрос перед отправкой его в СУБД. Система неявно добавляет в выборку скрытые поля: представления ссылок (для корректного отображения наименований), поля порядка (например, код или дату для сортировки) или уникальные идентификаторы. Поскольку на уровне СУБД сравнение строк при ОБЪЕДИНИТЬ или РАЗЛИЧНЫЕ происходит по абсолютно всем выбранным полям (включая невидимые системные), строки перестают считаться идентичными, и СУБД возвращает дубли.
  2. Проблема составных типов данных. Обратите внимание на поле ВерсииОбъектов.Объект. В типовых конфигурациях это поле имеет составной тип данных (ссылается на множество различных справочников и документов). В то же время поле ВзаимодействияСКлиентомИстория.Ссылка имеет строго определенный тип. При объединении полей с разными типами данных (даже если в текущей строке фактически лежит ссылка нужного типа), СУБД и СКД могут по-разному формировать внутренние представления полей, из-за чего строки также перестают сворачиваться.
  3. Вывод через «Детальные записи». Если в настройках структуры отчета на вкладке «Настройки» вывод данных организован через элемент «Детальные записи», СКД просто транслирует полученные из базы строки без их группировки на уровне самого процессора компоновки. Любое минимальное расхождение в скрытых полях приведет к дублированию строк в готовом отчете.

Теперь, когда мы понимаем природу проблемы, рассмотрим практические способы её решения.

---

Способ 1. Настройка группировки в структуре компоновки (Рекомендуемый путь)

Самый простой и правильный с точки зрения архитектуры СКД способ избавиться от дублей — перенести задачу сворачивания строк с уровня запроса на уровень настроек компоновки данных. Вместо детального вывода строк мы заставим СКД сгруппировать данные.

Выполним следующие шаги по настройке:

  1. Перейдем на вкладку «Настройки» в схеме компоновки данных.
  2. В структуре отчета найдем элемент «Детальные записи», нажмем по нему правой кнопкой мыши и выберем «Удалить» (или изменим его тип).
  3. Добавим новую группировку: нажмем «Добавить» -> «Новая группировка...».
  4. В открывшемся окне в качестве поля группировки выберем наше поле Документ.
  5. Перейдем на вкладку «Выбранные поля» для этой группировки и перетащим туда поле Документ.

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

---

Способ 2. Явное приведение типов с помощью функции ВЫРАЗИТЬ

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

Давайте проанализируем, как должен измениться наш запрос во втором пакете:


////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВЫРАЗИТЬ(ВерсииОбъектов.Объект КАК Документ.ВзаимодействияСКлиентом) КАК Документ
ПОМЕСТИТЬ Версии
ИЗ
    РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
    НАЧАЛОПЕРИОДА(ВерсииОбъектов.ДатаВерсии, ДЕНЬ) МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
    И ВерсииОбъектов.Объект ССЫЛКА Документ.ВзаимодействияСКлиентом ;

Используя конструкцию ВЫРАЗИТЬ(... КАК ...), мы жестко указываем платформе, что поле должно обрабатываться как конкретная ссылка. Это существенно упрощает СУБД процесс сравнения и объединения строк в финальной таблице.

---

Способ 3. Отключение автозаполнения и ручная настройка полей

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

Для этого выполним по шагам:

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

Этот способ требует больше ручной работы, но он полностью блокирует попытки СКД «дописать» запрос своими полями, благодаря чему конструкции ОБЪЕДИНИТЬ и РАЗЛИЧНЫЕ начинают работать со стопроцентной точностью.

---

Способ 4. Группировка во временной таблице в пакете запроса

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

Рассмотрим пример реализации такого пакета запросов:


// 1. Получаем документы из истории изменений
ВЫБРАТЬ
    ВзаимодействияСКлиентомИстория.Ссылка КАК Документ
ПОМЕСТИТЬ ВТ_История
ИЗ
    Документ.ВзаимодействияСКлиентом.История КАК ВзаимодействияСКлиентомИстория
ГДЕ
    ВзаимодействияСКлиентомИстория.Дата МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
СГРУППИРОВАТЬ ПО
    ВзаимодействияСКлиентомИстория.Ссылка;

// 2. Получаем документы из версий объектов с приведением типов
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВЫРАЗИТЬ(ВерсииОбъектов.Объект КАК Документ.ВзаимодействияСКлиентом) КАК Документ
ПОМЕСТИТЬ ВТ_Версии
ИЗ
    РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
    ВерсииОбъектов.ДатаВерсии МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
    И ВерсииОбъектов.Объект ССЫЛКА Документ.ВзаимодействияСКлиентом;

// 3. Объединяем результаты во временную таблицу
ВЫБРАТЬ
    ВТ_История.Документ КАК Документ
ПОМЕСТИТЬ ВТ_ВсеОбъединенные
ИЗ
    ВТ_История КАК ВТ_История
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    ВТ_Версии.Документ
ИЗ
    ВТ_Версии КАК ВТ_Версии;

// 4. Финальный запрос: группируем объединенные данные для исключения дублей
ВЫБРАТЬ
    ВТ_ВсеОбъединенные.Документ КАК Документ
ИЗ
    ВТ_ВсеОбъединенные КАК ВТ_ВсеОбъединенные
СГРУППИРОВАТЬ ПО
    ВТ_ВсеОбъединенные.Документ

Важное примечание к этому способу: группировка в финальном запросе пакета (Шаг 4) сработает безупречно на уровне базы данных, но если в настройках отчета СКД все еще выбраны «Детальные записи» и включено автозаполнение, СКД все равно может попытаться разунифицировать эти строки на самом последнем этапе компоновки результата. Поэтому для достижения 100% результата рекомендуется комбинировать Способ 4 со Способом 1.

← На главную