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

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет
← На главную

При разработке сложных отчетов или печатных форм в системе 1С программисты часто сталкиваются с необходимостью получения данных из «цепочки» документов. Типичная ситуация: у нас есть основной документ, у которого может быть основание, а у этого основания — свое основание. В процессе построения таких связей в конструкторе запросов нередко возникает ошибка «Противоречивая связь». Это происходит из-за попытки системы выполнить сложное соединение таблиц, которое логически конфликтует с уже установленными связями.

В этой статье мы разберем конкретный пример, когда нужно получить данные из документа Регистрация несоответствия (РН), который может быть связан с документом Внутреннее потребление товаров (ВП) двумя разными путями. Мы научимся обходить технические ограничения платформы и строить эффективные запросы.

Разбор проблемы и сценариев связи

Рассмотрим ситуацию подробнее. Нам необходимо получить значение колонки Несоответствие из табличной части документа СТ_РегистрацияНесоответствий. Существует два варианта того, как эти документы могут быть связаны в базе данных:

  1. Вариант через промежуточное звено: Документ Внутреннее потребление создан на основании документа Заказ на внутреннее потребление (ЗаказНВП), который, в свою очередь, создан на основании документа Регистрация несоответствия.
  2. Прямой вариант: Документ Внутреннее потребление создан напрямую на основании документа Регистрация несоответствия, минуя этап заказа.

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

Решение через использование ОБЪЕДИНИТЬ ВСЕ

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

В первой части запроса мы опишем логику для первого варианта (через Заказ), а во второй — для прямого основания. Рассмотрим пример кода:


ВЫБРАТЬ
    ВП.Ссылка КАК СсылкаНаДокумент,
    СТ_Товары.Несоответствие КАК Несоответствие,
    ЗаказНВП.ДокументОснование КАК Основание
ИЗ
    Документ.ВнутреннееПотреблениеТоваров КАК ВП
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказНаВнутреннееПотребление КАК ЗаказНВП
        ПО ВП.ЗаказНаВнутреннееПотребление = ЗаказНВП.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.СТ_РегистрацияНесоответствий.Товары КАК СТ_Товары
        ПО ЗаказНВП.ДокументОснование = СТ_Товары.Ссылка
ГДЕ
    ВП.Ссылка = &Ссылка
    И ЗаказНВП.ДокументОснование ССЫЛКА Документ.СТ_РегистрацияНесоответствий

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

ВЫБРАТЬ
    ВП.Ссылка,
    СТ_Товары.Несоответствие,
    ВП.ДокументОснование
ИЗ
    Документ.ВнутреннееПотреблениеТоваров КАК ВП
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.СТ_РегистрацияНесоответствий.Товары КАК СТ_Товары
        ПО ВП.ДокументОснование = СТ_Товары.Ссылка
ГДЕ
    ВП.Ссылка = &Ссылка
    И ВП.ДокументОснование ССЫЛКА Документ.СТ_РегистрацияНесоответствий

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

Работа с типизацией полей (ВЫРАЗИТЬ и ССЫЛКА)

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

Чтобы избежать проблем, мы рекомендуем:

Рассмотрим пример правильного приведения типа в соединении:


ЛЕВОЕ СОЕДИНЕНИЕ Документ.СТ_РегистрацияНесоответствий.Товары КАК СТ_Товары
    ПО ВЫРАЗИТЬ(ВП.ДокументОснование КАК Документ.СТ_РегистрацияНесоответствий) = СТ_Товары.Ссылка

Проблема дублирования строк при соединении с табличными частями

Выясним причину возможного «размножения» строк в результате. Когда мы соединяем документ с его табличной частью (СТ_РегистрацияНесоответствий.Товары), на каждую строку из этой таблицы в результате запроса будет создана отдельная строка. Если в документе регистрации три строки с разными несоответствиями, то для одного документа Внутреннее потребление мы получим три записи.

Если для печатной формы вам нужно только одно (первое или уникальное) значение, следует использовать группировку или оператор РАЗЛИЧНЫЕ:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    СТ_Товары.Несоответствие КАК Несоответствие
ИЗ
    ...

Альтернативный подход: использование временных таблиц

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

Посмотрим на алгоритм реализации:

  1. Выбираем во временную таблицу ВТ_Основания ссылки на документы Внутреннее потребление и определяем для каждого из них «целевой» документ РН, используя функцию ЕСТЬNULL.
  2. Соединяем ВТ_Основания с табличной частью товаров документа СТ_РегистрацияНесоответствий.

Пример формирования временной таблицы:


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

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

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

← На главную