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