Например, нужно получить список всех товаров по складу, но если в качестве склада передано пустое значение, то необходимо получить список товаров по всем складам (т.е. склад не выбран).
Как правило в таких случаях модифицируют текст запроса "на лету" примерно таким образом:
Запрос.Текст ="ВЫБРАТЬ| Учет.Товар,| Учет.Количество|ИЗ| Документ.Учет КАК Учет|" + ?(ЗначениеЗаполнено(Склад),"ГДЕ Учет.Склад = &Склад","");
Без сомнений, такой способ имеет достоинства, но у него есть и существенный недостаток. При таком "ручном" вмешательстве в текст запроса, перестает работать очень удобный инструмент - конструктор запросов. Он просто не понимает наших "посторонних" включений. Особенно ярко это проявляется когда речь идет о "монструозных" запросах на несколько страниц. Сталкиваясь с таким великаном и обнаруживая в нем включения кода 1С, я всегда думаю такие плохие вещи, что будет неловко их тут повторять.
Однако, единственный ли это способ достичь желаемого, сделать так, чтобы склад не учитывался в отборе, если он не заполнен? Не единственный! Если немного пораскинуть мозгами очень просто перенести условную конструкцию внутрь самого запроса, например вот так:
Запрос.Текст ="ВЫБРАТЬ| Учет.Товар,| Учет.Количество|ИЗ| Документ.Учет КАК Учет|ГДЕ| (&Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)| ИЛИ Учет.Склад = &Склад)";
Никакого волшебства, всего-лишь банальное использование оператора "ИЛИ". Если склад не заполнен то и все выражение всегда будет истинным, а значит в выборку попадут все склады.
Конечно, такой способ несколько усложняет само условие запроса, но мне кажется, что это стоит того, чтобы получить преимущества предоставляемые конструктором.
Что же с производительностью? Действительно, производительность в таком случае немного падает. Я сделал небольшую конфигурацию, чтобы сделать замеры. Желающие могут скачать ее и попробовать померить производительность самостоятельно (1C 8.2.10.77).
Исходные данные: 20 документов, 7 товаров, 3 склада, ~10000 вызовов запроса (5000 с пустым и 5000 с заполненным складом).
Запрос в котором условие подставляется в текст средствами языка 1С:
Как видите, 10002 запроса (строка 15) выполняется за 8,2 секунды.
А вот, результаты выполнения запроса с внесенным внутрь условием:
Результат (59-я строка) выполнения - 11,4 секунды. То-есть 3,2 секунды на ~10000 запросов.
Можно было бы провести замеры с вложенными запросами и соединениями или с другими объемами данных, но в целом понятно что для простых случаев потеря производительности несущественная. К тому же запросы-монстры не часто вызываются по 10000 раз за один раз.
Товарищи мне подсказывают, что данные в таком случае берутся из кэша, поэтому производительность оценивается не совсем верно. Я предполагаю сделать еще замеров в будущем на разные способы и объемы данных или сделайте это сами.
Какой из способов использовать, конечно, решать вам.
А я хочу пожелать вам хорошего дня и хорошего кода.
Спасибо за внимание.
P.S.: Статья получила множество отзывов и уважаемое сообщество насоветовало еще кучу способов борьбы с условиями.
Уважаемый alexk-is советует использовать построитель отчета:
ПостроительОтчета.Текст =
"ВЫБРАТЬ
| Учет.Товар,
| Учет.Количество
|ИЗ
| Документ.Учет КАК Учет
|{ГДЕ
| Учет.Склад.*}";
ПостроительОтчета.ПолучитьЗапрос().Выполнить();
Yashazz ставит комментарии, выполняет поиск и замену в тексте запроса перед выполнением, но сокрушается, что конструктор режет комментарии:
ТекстЗапроса =
"ВЫБРАТЬ
| Учет.Товар,
| Учет.Количество
|ИЗ
| Документ.Учет КАК Учет
|ГДЕ
|Склад = &УсловныйСклад
|//УсловиеНаТовар//";
...
СтрЗаменить(ТекстЗапроса,"//УсловиеНаТовар//","И Товар = &УсловныйТовар");
Зато Alias прекрасно развивает идею и советует делать так:
Запрос.Текст =
"ВЫБРАТЬ
| Учет.Товар,
| Учет.Количество
|ИЗ
| Документ.Учет КАК Учет
|ГДЕ
| &УсловиеСклада";
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеСклада",?(ЗначениеЗаполнено(ВыбСклад),"Учет.Склад = &Склад","Истина"));
Без сомнения в способе куча плюсов - и конструктор не режет ничего и запрос читается нормально и тормозов нет.
4ishиспользует выбор:
Запрос.Текст =
"ВЫБРАТЬ
| Учет.Товар,
| Учет.Количество
|ИЗ
| Документ.Учет КАК Учет
|ГДЕ
|ВЫБОР
| КОГДА &Склад <> ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)
| ТОГДА Учет.Склад = &Склад
| ИНАЧЕ ИСТИНА
|КОНЕЦ";
Тоже хороший способ.
А Vit aka proger изящно применяет "в иерархии":
Запрос.Текст =
"ВЫБРАТЬ
| Учет.Товар,
| Учет.Количество
|ИЗ
| Документ.Учет КАК Учет
|ГДЕ
| Учет.Склад в иерархии( &Склад))";
Serj1С иногда использует:
Запрос.Текст =
"ВЫБРАТЬ
| Учет.Товар,
| Учет.Количество
|ИЗ
| Документ.Учет КАК Учет
|ГДЕ
| &Склад В (Учет.Склад, ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка))"
Всем спасибо за замечательные подсказки, я обязательно возьму кое-что себе на вооружение.
Ну, и, конечно обсуждение еще продолжается.