При разработке механизмов заполнения форм, создании обработок или формировании печатных форм программисты 1С часто сталкиваются с необходимостью извлечь данные из табличной части документа, при этом накладывая фильтр на реквизиты «шапки» этого же документа. Типичный пример — получение строк Заказа клиента, только если он находится в определенном статусе (например, статус не равен «Новый» или «Не согласован»).
В данной статье мы подробно разберем, где эффективнее писать условие — непосредственно в тексте запроса или уже при обработке его результата, — а также научимся правильно работать с типами Перечисление в обоих случаях.
Это наиболее правильный подход с точки зрения производительности системы. Если мы заранее знаем, что документы в определенном статусе нам не нужны, логично отсечь их на уровне СУБД, не передавая лишние данные в память сервера приложений 1С. Разберем, как это реализовать, работая с табличной частью.
Когда мы делаем запрос к табличной части (например, Документ.ЗаказКлиента.Товары), у каждой строки есть системное поле Ссылка. Через это поле мы можем обратиться к любому реквизиту основного документа, используя обращение «через точку».
Проанализируем пример кода запроса с использованием конструкции ЗНАЧЕНИЕ() для работы с перечислениями:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказКлиентаТовары.Ссылка КАК ДокументСсылка,
| ЗаказКлиентаТовары.Номенклатура КАК Номенклатура,
| ЗаказКлиентаТовары.Количество КАК Количество
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
|ГДЕ
| ЗаказКлиентаТовары.Ссылка.Статус <> ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Новый)";
РезультатЗапроса = Запрос.Выполнить();
Важные замечания к этому способу:
ЗНАЧЕНИЕ(Перечисление.Имя.Значение) позволяет жестко зафиксировать условие в коде запроса. Это удобно, если логика программы не предполагает динамического изменения фильтра.ЗаказКлиентаТовары.Ссылка.Статус создает в SQL-запросе неявное соединение (Left Join) табличной части с таблицей документа. На больших объемах данных это допустимо, но следует помнить об индексации реквизита Статус в метаданных для ускорения поиска.ГДЕ допущена ошибка (например, как в одном из сообщений форума, где забыли указать поле .Статус после .Ссылка), запрос вернет пустой результат или ошибку несоответствия типов, так как Ссылка — это объект, а ЗНАЧЕНИЕ(...) — это конкретный элемент перечисления.Более гибким и профессиональным подходом считается использование параметров. Это избавляет от «хардкода» (жесткого прописывания путей к перечислениям) в тексте запроса и позволяет легко изменять условия фильтрации во встроенном языке.
Рассмотрим, как изменить предыдущий пример:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказКлиентаТовары.Ссылка КАК ДокументСсылка,
| ЗаказКлиентаТовары.Номенклатура КАК Номенклатура
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
|ГДЕ
| ЗаказКлиентаТовары.Ссылка.Статус <> &ИсключаемыйСтатус";
Запрос.УстановитьПараметр("ИсключаемыйСтатус", Перечисления.СтатусыЗаказовКлиентов.Новый);
РезультатЗапроса = Запрос.Выполнить();
Этот метод предпочтителен, если вы планируете выносить логику в общие модули или работать с несколькими возможными статусами одновременно (например, используя оператор В (&СписокСтатусов)).
Иногда возникает ситуация, когда нам нужно не просто отфильтровать данные, а выполнить определенное действие в зависимости от статуса — например, вывести предупреждение пользователю или «закошмарить» ответственный отдел (как выразился автор темы). В этом случае фильтрация внутри ГДЕ не подойдет, так как «плохие» документы просто не попадут в выборку, и мы не сможем их проанализировать.
Разберем алгоритм, при котором мы получаем все данные, но обрабатываем их выборочно:
// В запросе выбираем статус как отдельное поле
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказКлиентаТовары.Ссылка КАК Ссылка,
| ЗаказКлиентаТовары.Ссылка.Статус КАК Статус
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.Статус = Перечисления.СтатусыЗаказовКлиентов.Новый Тогда
// Здесь мы можем уведомить пользователя
Сообщить("Документ " + Выборка.Ссылка + " находится в статусе Новый. Обработка невозможна!");
Продолжить; // Переходим к следующей строке выборки
КонецЕсли;
// Выполняем полезные действия с результатом
СоздатьКнопкуДляСтроки(Выборка);
КонецЦикла;
Важно: Обратите внимание на синтаксис Если Статус <> Новый Тогда. В языке 1С слово Новый является зарезервированным оператором для создания объектов (например, Новый Массив). Поэтому при сравнении всегда следует обращаться к полному имени значения через Перечисления.ИмяПеречисления.Значение.
Если ваша задача — создать динамический интерфейс (например, кнопки для каждой строки ТЧ), но при этом исключить документы в статусе «Новый», рассмотрите комбинированный подход. Выясним причину, по которой пользователь может не увидеть данные, добавив в запрос специальный флаг.
Проанализируем пример запроса с конструкцией ВЫБОР:
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказКлиентаТовары.Ссылка КАК Ссылка,
| ВЫБОР
| КОГДА ЗаказКлиентаТовары.Ссылка.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовКлиентов.Новый)
| ТОГДА ИСТИНА
| ИНАЧЕ ЛОЖЬ
| КОНЕЦ КАК ЭтоЧерновик
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары";
// При обходе результата:
Если Выборка.ЭтоЧерновик Тогда
// Рисуем заблокированную кнопку или пишем пояснение
Иначе
// Рисуем активную кнопку
КонецЕсли;
Такой подход позволяет избежать ситуации, когда пользователь видит пустой экран и не понимает, почему система не предлагает ему действий. Прозрачность логики — ключ к качественной разработке.
Рассмотрим напоследок важное замечание по поводу «кошерности» запросов. В системе 1С запросы напрямую к табличным частям документов для получения оперативных данных считаются менее эффективными, чем запросы к Регистрам накопления или Регистрам сведений. Документы — это объекты регистрации событий, их таблицы могут быть заблокированы при активной записи.
Если ваша задача позволяет (например, нужно получить остатки по заказам), всегда старайтесь обращаться к соответствующим регистрам. Однако для интерфейсных задач (как в случае автора темы — создание кнопок на форме обработки), работа с табличными частями напрямую является оправданным и стандартным решением.
Резюмируем:
ГДЕ в запросе и параметры.Пока Выборка.Следующий().