При разработке отчетов на базе Системы компоновки данных (СКД) в 1С программисты часто сталкиваются с ситуацией, когда визуально одинаковые строки или документы дублируются в итоговом результате. Особенно ярко эта проблема проявляется при использовании конструкций ОБЪЕДИНИТЬ или ОБЪЕДИНИТЬ ВСЕ во временных таблицах или вложенных запросах.
Казалось бы, использование ключевого слова РАЗЛИЧНЫЕ или замена ОБЪЕДИНИТЬ ВСЕ на ОБЪЕДИНИТЬ (которое по умолчанию должно сворачивать одинаковые строки) должны решить проблему. Однако в СКД отчет все равно продолжает выводить дублирующиеся документы. В этой статье мы подробно разберем, почему так происходит, и рассмотрим четыре надежных способа устранения дублей.
Рассмотрим стандартную ситуацию. Нам необходимо собрать документы из двух источников: табличной части «История» документа ВзаимодействияСКлиентом и регистра сведений ВерсииОбъектов. Автор темы на форуме попытался объединить их следующим запросом:
ВЫБРАТЬ
ВзаимодействияСКлиентомИстория.Ссылка КАК Документ
ПОМЕСТИТЬ Взаимод
ИЗ
Документ.ВзаимодействияСКлиентом.История КАК ВзаимодействияСКлиентомИстория
ГДЕ
НАЧАЛОПЕРИОДА(ВзаимодействияСКлиентомИстория.Дата, ДЕНЬ) МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
СГРУППИРОВАТЬ ПО
ВзаимодействияСКлиентомИстория.Ссылка ;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВерсииОбъектов.Объект ПОМЕСТИТЬ Версии
ИЗ
РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
НАЧАЛОПЕРИОДА(ВерсииОбъектов.ДатаВерсии, ДЕНЬ) МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
И ВерсииОбъектов.Объект ССЫЛКА Документ.ВзаимодействияСКлиентом ;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Версии.Объект КАК Документ
ИЗ
Версии КАК Версии
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Взаимод.Документ ИЗ Взаимод КАК Взаимод
При выводе этого отчета один и тот же документ выводится дважды. Даже замена конструкций или добавление ВЫБРАТЬ РАЗЛИЧНЫЕ в финальном пакете запроса не решают проблему до конца. На это есть три скрытые причины, связанные с внутренней логикой работы СКД.
ОБЪЕДИНИТЬ или РАЗЛИЧНЫЕ происходит по абсолютно всем выбранным полям (включая невидимые системные), строки перестают считаться идентичными, и СУБД возвращает дубли.ВерсииОбъектов.Объект. В типовых конфигурациях это поле имеет составной тип данных (ссылается на множество различных справочников и документов). В то же время поле ВзаимодействияСКлиентомИстория.Ссылка имеет строго определенный тип. При объединении полей с разными типами данных (даже если в текущей строке фактически лежит ссылка нужного типа), СУБД и СКД могут по-разному формировать внутренние представления полей, из-за чего строки также перестают сворачиваться.Теперь, когда мы понимаем природу проблемы, рассмотрим практические способы её решения.
---
Самый простой и правильный с точки зрения архитектуры СКД способ избавиться от дублей — перенести задачу сворачивания строк с уровня запроса на уровень настроек компоновки данных. Вместо детального вывода строк мы заставим СКД сгруппировать данные.
Выполним следующие шаги по настройке:
Документ.Документ.В этом случае, даже если результирующий запрос вернет дублирующиеся строки из-за разницы в скрытых системных полях, процессор компоновки данных гарантированно выполнит группировку по полю Документ и выведет каждую ссылку ровно один раз.
---
Чтобы нивелировать разницу типов данных между строго типизированной ссылкой документа и составным типом поля Объект из регистра сведений, необходимо использовать явное приведение типов в тексте запроса. Это поможет оптимизировать работу СУБД и исключит дубли, вызванные неявным приведением типов.
Давайте проанализируем, как должен измениться наш запрос во втором пакете:
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВЫРАЗИТЬ(ВерсииОбъектов.Объект КАК Документ.ВзаимодействияСКлиентом) КАК Документ
ПОМЕСТИТЬ Версии
ИЗ
РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
НАЧАЛОПЕРИОДА(ВерсииОбъектов.ДатаВерсии, ДЕНЬ) МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
И ВерсииОбъектов.Объект ССЫЛКА Документ.ВзаимодействияСКлиентом ;
Используя конструкцию ВЫРАЗИТЬ(... КАК ...), мы жестко указываем платформе, что поле должно обрабатываться как конкретная ссылка. Это существенно упрощает СУБД процесс сравнения и объединения строк в финальной таблице.
---
Если вам критически важно, чтобы запрос выполнялся на уровне СУБД ровно в том виде, в котором он написан в конфигураторе (без добавления скрытых полей сортировки и представлений), вы можете отключить автозаполнение.
Для этого выполним по шагам:
Этот способ требует больше ручной работы, но он полностью блокирует попытки СКД «дописать» запрос своими полями, благодаря чему конструкции ОБЪЕДИНИТЬ и РАЗЛИЧНЫЕ начинают работать со стопроцентной точностью.
---
Если вам необходимо передать в СКД уже чистые, гарантированно свернутые данные, и вы хотите сделать это именно средствами языка запросов, лучшим решением будет помещение результатов объединения во временную таблицу с последующей группировкой.
Рассмотрим пример реализации такого пакета запросов:
// 1. Получаем документы из истории изменений
ВЫБРАТЬ
ВзаимодействияСКлиентомИстория.Ссылка КАК Документ
ПОМЕСТИТЬ ВТ_История
ИЗ
Документ.ВзаимодействияСКлиентом.История КАК ВзаимодействияСКлиентомИстория
ГДЕ
ВзаимодействияСКлиентомИстория.Дата МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
СГРУППИРОВАТЬ ПО
ВзаимодействияСКлиентомИстория.Ссылка;
// 2. Получаем документы из версий объектов с приведением типов
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВЫРАЗИТЬ(ВерсииОбъектов.Объект КАК Документ.ВзаимодействияСКлиентом) КАК Документ
ПОМЕСТИТЬ ВТ_Версии
ИЗ
РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
ГДЕ
ВерсииОбъектов.ДатаВерсии МЕЖДУ &ДатаВерсииН И &ДатаВерсииО
И ВерсииОбъектов.Объект ССЫЛКА Документ.ВзаимодействияСКлиентом;
// 3. Объединяем результаты во временную таблицу
ВЫБРАТЬ
ВТ_История.Документ КАК Документ
ПОМЕСТИТЬ ВТ_ВсеОбъединенные
ИЗ
ВТ_История КАК ВТ_История
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ВТ_Версии.Документ
ИЗ
ВТ_Версии КАК ВТ_Версии;
// 4. Финальный запрос: группируем объединенные данные для исключения дублей
ВЫБРАТЬ
ВТ_ВсеОбъединенные.Документ КАК Документ
ИЗ
ВТ_ВсеОбъединенные КАК ВТ_ВсеОбъединенные
СГРУППИРОВАТЬ ПО
ВТ_ВсеОбъединенные.Документ
Важное примечание к этому способу: группировка в финальном запросе пакета (Шаг 4) сработает безупречно на уровне базы данных, но если в настройках отчета СКД все еще выбраны «Детальные записи» и включено автозаполнение, СКД все равно может попытаться разунифицировать эти строки на самом последнем этапе компоновки результата. Поэтому для достижения 100% результата рекомендуется комбинировать Способ 4 со Способом 1.