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