Почему полное соединение в запросе 1С работает как левое и как это исправить?

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

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

Причина превращения полного соединения в левое

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


ИЗ
    РегистрБухгалтерии.Хозрасчетный.Остатки(&ДатаОтчета, , , Организация = &Организация) КАК ХозрасчетныйОстатки
        ПОЛНОЕ СОЕДИНЕНИЕ ДанныеБП КАК ДанныеБП
        ПО ХозрасчетныйОстатки.Субконто1.Артикул = ДанныеБП.КодТовара
ГДЕ
    ХозрасчетныйОстатки.Счет = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ТоварыНаСкладах)

Выясним причину некорректного поведения. Конструкция ПОЛНОЕ СОЕДИНЕНИЕ действительно формирует объединенный набор данных. Для тех строк из таблицы ДанныеБП, которым не нашлось соответствия в регистре, поля таблицы ХозрасчетныйОстатки заполняются значениями NULL.

После этапа соединения СУБД применяет фильтр из секции ГДЕ. Когда условие проверяет поле ХозрасчетныйОстатки.Счет = ЗНАЧЕНИЕ(...), для строк с отсутствующими остатками значением поля является NULL. Сравнение NULL = Значение возвращает Ложь (или UNKNOWN), и все строки из правой таблицы, не имеющие пары слева, попросту отсекаются. Таким образом, условие в секции ГДЕ превращает полное соединение в обычное левое соединение.

Решение 1. Вынос наборов данных в предварительные временные таблицы

Самый надежный и производительный способ решения проблемы — подготовить данные каждой таблицы изолированно до момента их соединения. Разберем этот алгоритм:

  1. Выбираем и группируем первую таблицу данных (например, из внешнего источника) и помещаем во временную таблицу ДанныеБП.
  2. Выбираем данные регистра бухгалтерии в отдельную временную таблицу ДанныеКА, накладывая все фильтры по счетам и субконто непосредственно в параметрах виртуальной таблицы либо в секции ГДЕ этой выборки.
  3. В финальном запросе выполняем полное соединение уже отфильтрованных и сгруппированных временных таблиц без секции ГДЕ.

Посмотрим на пример формирования таблицы остатков регистра:


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

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

Решение 2. Обход проверки на NULL в секции ГДЕ

Если по каким-то причинам вынести данные во временную таблицу невозможно, можно изменить логику условия в секции ГДЕ, явно разрешив значение NULL:


ГДЕ
    (ХозрасчетныйОстатки.Счет ЕСТЬ NULL
        ИЛИ ХозрасчетныйОстатки.Счет = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ТоварыНаСкладах))

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

Устранение ошибки «Противоречивая связь» при подключении номенклатуры

Когда к результату полного соединения двух таблиц требуется добавить ссылку на справочник Номенклатура по артикулу, разработчики часто пытаются связать справочник одновременно и с первой, и со второй таблицей через левые соединения. Это приводит к синтаксической ошибке платформы «Противоречивая связь», так как СУБД не может однозначно определить порядок обхода связей.

Рассмотрим правильные варианты добавления номенклатуры:

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

Особенности расчета себестоимости и отклонений

Обратим внимание на частую математическую ошибку при расчете средних показателей в запросах. Если рассчитывать среднюю себестоимость внутри функции агрегации как СУММА(СуммаОстаток / КоличествоОстаток), запрос выдаст сумму средних цен отдельных записей, что приведет к искажению итогов. Корректный расчет средневзвешенной цены всегда выполняется путем деления предварительно просуммированных итогов:


ВЫБОР
    КОГДА СУММА(ХозрасчетныйОстатки.КоличествоОстаток) <> 0
        ТОГДА ВЫРАЗИТЬ(СУММА(ХозрасчетныйОстатки.СуммаОстаток) / СУММА(ХозрасчетныйОстатки.КоличествоОстаток) КАК ЧИСЛО(15, 2))
    ИНАЧЕ 0
КОНЕЦ КАК СебестоимостьКА

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

← На главную