Как в запросе 1С вывести два связанных документа в одной строке без дублей?

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

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

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

Разбор типичных ошибок и причин их возникновения

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

Ошибка 1. Неоптимальное ПОЛНОЕ СОЕДИНЕНИЕ с разыменованием полей

Часто разработчики пытаются связать две таблицы документов с помощью конструкции ПОЛНОЕ СОЕДИНЕНИЕ, накладывая жесткое двустороннее условие связи через точку:


ВЫБРАТЬ
	Начало.Ссылка КАК НачалоРемонта,
	Окончание.Ссылка КАК ОкончаниеРемонта
ИЗ
	Документ.НачалоРемонта КАК Начало
	ПОЛНОЕ СОЕДИНЕНИЕ Документ.ОкончаниеРемонта КАК Окончание
		ПО Начало.Ссылка.ОкончаниеРемонта = Окончание.Ссылка 
		И Начало.Ссылка = Окончание.Ссылка.НачалоРемонта

Выделим две основные причины, почему этот запрос является крайне неудачным:

  1. Логическая ошибка (условие "И"): Если хотя бы в одном из документов связь заполнена, а во втором — нет (например, в «Начале» указали ссылку на «Окончание», но в самом «Окончании» обратную ссылку еще не успели или забыли проставить), то жесткое условие И (AND) не выполнится. СУБД не сможет сопоставить эти записи в одну строку. В результате они выведутся в разных строках (каждая со своим значением NULL во второй колонке), что визуально воспринимается пользователем как дублирование информации.
  2. Грубое нарушение стандартов производительности 1С: Обращение к реквизитам через точку в секции соединения (Начало.Ссылка.ОкончаниеРемонта) заставляет платформу генерировать неявные левые соединения (LEFT JOIN) на уровне СУБД. Это приводит к созданию крайне неоптимального плана запроса, полному сканированию таблиц и колоссальной нагрузке на сервер при росте объема базы данных.

Ошибка 2. Локальная группировка внутри объединения таблиц

Еще одна распространенная попытка обойти проблему дублей — объединение выборок через ОБЪЕДИНИТЬ ВСЕ с группировкой внутри каждого подзапроса:


ВЫБРАТЬ
	НачалоРемонта.Ссылка КАК НачалоРемонта,
	НачалоРемонта.ОкончаниеРемонта КАК ОкончаниеРемонта
ИЗ
	Документ.НачалоРемонта КАК НачалоРемонта
СГРУППИРОВАТЬ ПО
	НачалоРемонта.Ссылка,
	НачалоРемонта.ОкончаниеРемонта

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	ОкончаниеРемонта.НачалоРемонта,
	ОкончаниеРемонта.Ссылка
ИЗ
	Документ.ОкончаниеРемонта КАК ОкончаниеРемонта
СГРУППИРОВАТЬ ПО
	ОкончаниеРемонта.НачалоРемонта,
	ОкончаниеРемонта.Ссылка

Выясним причину, почему этот код все равно возвращает дублирующиеся строки. Оператор СГРУППИРОВАТЬ ПО в данном случае применяется локально к каждой таблице до того, как они будут объединены. СУБД сначала группирует строки внутри документов начала ремонта, затем отдельно группирует строки окончания ремонта, а затем просто "склеивает" полученные результаты по вертикали. Если у нас есть полностью связанная пара документов, она попадет в итоговую таблицу дважды: первый раз из верхней ветки запроса, второй раз — из нижней.

---

Вариант А. Объединение с последующей группировкой (Рекомендуемый способ)

Рассмотрим наиболее производительный и надежный метод решения нашей задачи. Мы откажемся от тяжелого и капризного оператора ПОЛНОЕ СОЕДИНЕНИЕ в пользу вертикального объединения таблиц с последующим "схлопыванием" дублей единой группировкой по всему результату. Чтобы запрос работал максимально быстро, задействуем механизм временных таблиц.

Разберем этот метод по шагам на конкретном примере:


// Шаг 1: Собираем все связи в одну общую временную таблицу без использования точек
ВЫБРАТЬ
	Начало.Ссылка КАК НачалоРемонта,
	Начало.ОкончаниеРемонта КАК ОкончаниеРемонта
ПОМЕСТИТЬ ВТ_ВсеСвязи
ИЗ
	Документ.НачалоРемонта КАК Начало

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	Окончание.НачалоРемонта,
	Окончание.Ссылка
ИЗ
	Документ.ОкончаниеРемонта КАК Окончание
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Группируем полученные данные по обеим колонкам, убирая любые дубли
ВЫБРАТЬ
	ВТ_ВсеСвязи.НачалоРемонта КАК НачалоРемонта,
	ВТ_ВсеСвязи.ОкончаниеРемонта КАК ОкончаниеРемонта
ИЗ
	ВТ_ВсеСвязи КАК ВТ_ВсеСвязи
СГРУППИРОВАТЬ ПО
	ВТ_ВсеСвязи.НачалоРемонта,
	ВТ_ВсеСвязи.ОкончаниеРемонта

Проанализируем, как работает эта схема:

Важное преимущество для баз под управлением PostgreSQL: Фирма «1С» настоятельно рекомендует избегать использования полных соединений на этой СУБД, так как они выполняются значительно медленнее, чем на MS SQL Server. Использование временных таблиц с последующей группировкой — это кроссплатформенный и самый отказоустойчивый вариант решения задачи.

---

Вариант Б. Корректное ПОЛНОЕ СОЕДИНЕНИЕ (без разыменования полей)

Если бизнес-логика вашей конфигурации построена по стандартному принципу ввода на основании (то есть документ ОкончаниеРемонта всегда хранит ссылку на документ-основание НачалоРемонта в своем физическом реквизите), мы можем использовать полное соединение.

Давайте посмотрим на пример правильного написания такого запроса:


ВЫБРАТЬ
	Начало.Ссылка КАК НачалоРемонта,
	Окончание.Ссылка КАК ОкончаниеРемонта
ИЗ
	Документ.НачалоРемонта КАК Начало
	ПОЛНОЕ СОЕДИНЕНИЕ Документ.ОкончаниеРемонта КАК Окончание
		ПО Начало.Ссылка = Окончание.НачалоРемонта

Почему этот вариант корректен и эффективен:

---

Важные рекомендации и стандарты разработки 1С

Для создания качественных, быстродействующих и отказоустойчивых решений всегда придерживайтесь следующих архитектурных правил платформы 1С:

  1. Никогда не используйте оператор ИЛИ (OR) в секциях связи таблиц: Попытки связать таблицы условием вида ПО (Начало.Ссылка = Окончание.НачалоРемонта ИЛИ Начало.ОкончаниеРемонта = Окончание.Ссылка) приводят к тому, что оптимизатор СУБД полностью отказывается от использования индексов. База данных переходит в режим полного сканирования таблиц (Nested Loops), что на реальных объемах данных гарантированно приведет к зависанию запроса и блокировкам.
  2. Избегайте избыточного дублирования связей в метаданных: Хранение перекрестных ссылок друг на друга в двух разных документах одновременно — это классический антипаттерн проектирования баз данных. При таком подходе крайне сложно поддерживать актуальность и транзакционную целостность данных (при перезаполнении или удалении одного документа легко забыть обновить ссылку во втором).
  3. Используйте Критерии отбора для анализа связей: Если вам необходимо показать пользователю в интерфейсе красивое дерево подчиненных или связанных документов, не пишите сложные запросы вручную. Используйте типовой объект конфигурации Критерии отбора (например, стандартный критерий СвязанныеДокументы, присутствующий во многих типовых решениях 1С).
← На главную