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

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

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

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

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

Проанализируем типичную ошибку в структуре запроса, когда данные выбираются пакетами через временные таблицы:

  1. Отсутствие связи по идентификатору строки (КлючСтроки / КлючСвязи). В типовых конфигурациях 1С строки разных табличных частей одного документа логически связаны между собой не только ссылкой на документ, но и специальным числовым или уникальным идентификатором — КлючСтроки. Если соединять таблицы исключительно по условию Таблица1.Ссылка = Таблица2.Ссылка, каждая строка из первой табличной части соединится со всеми строками второй табличной части этого же документа.
  2. Неявное декартово произведение (Cross Join). Если в секции ИЗ таблицы перечисляются через запятую (например, ИЗ ЧФСКлючом КАК ЧФСКлючом, Товары КАК Товары) и не задано условие в секции ГДЕ или явное СОЕДИНЕНИЕ, СУБД выполняет прямое перемножение строк двух таблиц.
  3. Неправильный выбор типа соединения в Конструкторе запросов. Использование ЛЕВОЕ СОЕДИНЕНИЕ или установка двух флажков (что формирует ПОЛНОЕ СОЕДИНЕНИЕ) без ограничения по ключам связей приводит к многократному увеличению результирующей выборки.

Разбор работы Конструктора запросов при настройке связей

Рассмотрим, как Конструктор запросов интерпретирует флажки на вкладке Связи:

  1. Ни один флажок не установлен: формируется ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN). В результат попадут только те строки, для которых условие связи строго выполняется в обеих таблицах.
  2. Установлен флажок слева: формируется ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN). Выбираются все строки из левой таблицы, а поля правой таблицы заполняются значениями NULL, если соответствие не найдено.
  3. Установлены оба флажка: формируется ПОЛНОЕ СОЕДИНЕНИЕ (FULL JOIN). В выборку попадают все строки из обеих таблиц, что при отсутствии точных условий связи по ключам строк порождает тысячи избыточных записей.

Вариант 1. Оптимальное прямое соединение табличных частей без временных таблиц

Если задача заключается только в получении строк номенклатуры и соответствующего счета-фактуры из документа, разделение запроса на временные таблицы является избыточным. Проанализируем правильный вариант с прямым внутренним соединением табличных частей по двум полям:


ВЫБРАТЬ
    Товары.Ссылка КАК Ссылка,
    Товары.НомерСтроки КАК НомерСтроки,
    Товары.Номенклатура КАК Номенклатура,
    Товары.Количество КАК Количество,
    Поставщики.СчетФактура КАК СчетФактура
ИЗ
    Документ.ОтчетКомитентуОПродажах.Товары КАК Товары
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ОтчетКомитентуОПродажах.Поставщики КАК Поставщики
        ПО Товары.Ссылка = Поставщики.Ссылка
        И Товары.КлючСтроки = Поставщики.КлючСтроки
ГДЕ
    Поставщики.СчетФактура = &ВыбСФ

В данном запросе мы явно указываем ВНУТРЕННЕЕ СОЕДИНЕНИЕ и связываем таблицы одновременно по документу (Ссылка) и по номеру позиции внутри документа (КлючСтроки). Это исключает задвоение строк и сразу отсекает документы, не подходящие под условие отбора.

Вариант 2. Корректное использование временных таблиц

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


ВЫБРАТЬ
    Поставщики.Ссылка КАК Ссылка,
    Поставщики.КлючСтроки КАК КлючСтроки,
    Поставщики.СчетФактура КАК СчетФактура
ПОМЕСТИТЬ ВТ_Поставщики
ИЗ
    Документ.ОтчетКомитентуОПродажах.Поставщики КАК Поставщики
ГДЕ
    Поставщики.СчетФактура = &ВыбСФ
ИНДЕКСИРОВАТЬ ПО
    Ссылка,
    КлючСтроки
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Товары.Ссылка КАК Ссылка,
    Товары.НомерСтроки КАК НомерСтроки,
    Товары.Номенклатура КАК Номенклатура,
    Товары.Количество КАК Количество,
    ВТ_Поставщики.СчетФактура КАК СчетФактура
ИЗ
    Документ.ОтчетКомитентуОПродажах.Товары КАК Товары
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Поставщики КАК ВТ_Поставщики
        ПО Товары.Ссылка = ВТ_Поставщики.Ссылка
        И Товары.КлючСтроки = ВТ_Поставщики.КлючСтроки

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

  1. В первой временной таблице ВТ_Поставщики сохранено поле КлючСтроки.
  2. Добавлена директива ИНДЕКСИРОВАТЬ ПО для ускорения операции соединения на больших объемах данных.
  3. Исключен лишний промежуточный пакет, а в итоговом запросе заменено перечисление таблиц через запятую на явное ВНУТРЕННЕЕ СОЕДИНЕНИЕ по полям Ссылка и КлючСтроки.
← На главную