Как реализовать отбор в запросе 1С по номеру отгрузки из табличной части документа

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

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

Простая передача массива номеров в параметрах запроса не решит задачу, так как в этом случае выберутся все документы, где встречается хотя бы один номер из списка, что приведет к перемешиванию данных. Разберем по шагам, как правильно реализовать «парный» отбор (Ссылка на документ + Номер отгрузки), чтобы запрос работал корректно и эффективно.

Проблема использования простых массивов в условиях запроса

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


ГДЕ
    ТабличнаяЧасть.Ссылка В (&МассивДокументов)
    И ТабличнаяЧасть.НомерОтгрузки В (&МассивНомеров)

Выясним причину, почему это не работает для нашей задачи. Если у нас есть Документ №1 с номером отгрузки "100" и Документ №2 с номером "200", а мы хотим выбрать именно их, то при наличии в Документе №1 строки с номером "200", она также попадет в выборку. Это происходит потому, что условия накладываются независимо. Нам же необходимо жестко связать конкретную ссылку с конкретным номером.

Решение №1: Использование составного условия В с временной таблицей

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

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


// Подготовим таблицу с фильтрами
ТзОтбора = Новый ТаблицаЗначений;
ТзОтбора.Колонки.Добавить("ДокументСсылка", Новый ОписаниеТипов("ДокументСсылка.ХХХ_НарядЗадание"));
ТзОтбора.Колонки.Добавить("НомерОтгрузки", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(20)));

// Заполняем ТЗ данными из источника (например, из другой формы или обработки)
Для Каждого СтрокаИсточника Из ИсточникДанных Цикл
    НоваяСтрока = ТзОтбора.Добавить();
    НоваяСтрока.ДокументСсылка = СтрокаИсточника.Ссылка;
    НоваяСтрока.НомерОтгрузки = СтрокаИсточника.НомерОтгрузки;
КонецЦикла;

// Крайне важно убрать дубли, чтобы не нагружать запрос
ТзОтбора.Свернуть("ДокументСсылка, НомерОтгрузки");

Теперь проанализируем структуру запроса. Мы помещаем эту таблицу во временную таблицу (ВТ) и используем её в условии основного запроса. Посмотрим на пример реализации:


ВЫБРАТЬ
    Тз_Фильтр.ДокументСсылка КАК ДокументСсылка,
    Тз_Фильтр.НомерОтгрузки КАК НомерОтгрузки
ПОМЕСТИТЬ ВТ_Фильтр
ИЗ
    &ТзОтбора КАК Тз_Фильтр
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Тч.Номенклатура КАК Номенклатура,
    Тч.Количество КАК Количество,
    Тч.Ссылка КАК ДокументСсылка
ИЗ
    Документ.ХХХ_НарядЗадание.ОтгруженоЗаказчиком КАК Тч
ГДЕ
    (Тч.Ссылка, Тч.НомерОтгрузки) В
        (ВЫБРАТЬ
            ВТ.ДокументСсылка,
            ВТ.НомерОтгрузки
        ИЗ
            ВТ_Фильтр КАК ВТ)

Важный момент: использование конструкции (Поле1, Поле2) В (ВЫБРАТЬ...) позволяет SQL-серверу выполнить эффективное сопоставление данных. Это гарантирует, что из таблицы будет выбрана строка только в том случае, если оба поля совпали с парой из нашей временной таблицы.

Решение №2: Использование Внутреннего Соединения (Inner Join)

Если по каким-то причинам использование подзапроса в условии ГДЕ кажется неудобным (например, при сложной динамической компоновке текста запроса), можно воспользоваться Внутренним соединением. Рассмотрим этот вариант подробнее.

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


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

Выясним преимущества и недостатки этого метода:

  1. Преимущество: Это зачастую самый быстрый способ обработки данных на уровне SQL-сервера, особенно если временная таблица проиндексирована.
  2. Риск: Если в вашей временной таблице ВТ_Фильтр окажутся дублирующиеся строки (одинаковые пары Ссылка + Номер), то записи из документа "размножатся" в результате соединения. Именно поэтому перед передачей таблицы в запрос всегда нужно использовать метод Свернуть() или ключевое слово РАЗЛИЧНЫЕ.

Оптимизация производительности: Индексы и Группировки

Когда мы работаем с такими тяжелыми документами, как ЭтапПроизводства2_2 (встречается в ERP), объем данных в табличных частях может достигать десятков тысяч строк. В таких условиях скорость выполнения запроса становится критичной. Рассмотрим несколько советов по оптимизации.

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


ВЫБРАТЬ
    Тз.ДокументСсылка,
    Тз.НомерОтгрузки
ПОМЕСТИТЬ ВТ_Фильтр
ИЗ
    &Тз КАК Тз
ИНДЕКСИРОВАТЬ ПО
    ДокументСсылка,
    НомерОтгрузки

Во-вторых, если в итоговой выборке вы используете агрегатные функции (например, МАКСИМУМ или СУММА), как это было показано в финальных сообщениях темы, обязательно используйте СГРУППИРОВАТЬ ПО. Это поможет избежать "задвоения" записей, если логика документа допускает наличие нескольких строк с одинаковой номенклатурой и номером отгрузки в рамках одного документа.


ВЫБРАТЬ
    Тч.Номенклатура,
    МАКСИМУМ(Тч.Количество) КАК Количество,
    Тч.Ссылка
ИЗ
    Документ.ХХХ_НарядЗадание.ОтгруженоЗаказчиком КАК Тч
ГДЕ
    (Тч.Ссылка, Тч.НомерОтгрузки) В (ВЫБРАТЬ ВТ.ДокументСсылка, ВТ.НомерОтгрузки ИЗ ВТ_Фильтр КАК ВТ)
СГРУППИРОВАТЬ ПО
    Тч.Номенклатура,
    Тч.Ссылка

Подведение итогов

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

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

← На главную