понедельник, 18 октября 2010 г.

Условия и запросы

При составлении запросов часто бывает так, что нужно вставить условие отбора в запрос в зависимости от того, пустое значение отбора или оно заполнено.

Например, нужно получить список всех товаров по складу, но если в качестве склада передано пустое значение, то необходимо получить список товаров по всем складам (т.е. склад не выбран).

Как правило в таких случаях модифицируют текст запроса "на лету" примерно таким образом:
Запрос.Текст =
"ВЫБРАТЬ
|   Учет.Товар,
|   Учет.Количество
|ИЗ
|   Документ.Учет КАК Учет
|"
+ ?(ЗначениеЗаполнено(Склад),"ГДЕ Учет.Склад.Ссылка = &Склад","");

Без сомнений, такой способ имеет достоинства, но у него есть и существенный недостаток. При таком "ручном" вмешательстве в текст запроса, перестает работать очень удобный инструмент - конструктор запросов. Он просто не понимает наших "посторонних" включений. Особенно ярко это проявляется когда речь идет о "моструозных" запросах на несколько страниц. Сталкиваясь с таким великаном и обнаруживая в нем включения кода 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 изящно применяет "в иерархии":
Запрос.Текст =
"ВЫБРАТЬ
| Учет.Товар,
| Учет.Количество
|ИЗ
| Документ.Учет КАК Учет
|ГДЕ
| Учет.Склад в иерархии( &Склад))"
;
Всем спасибо за замечательные подсказки, я обязательно возьму кое-что себе на вооружение.
Ну, и, конечно обсуждение еще продолжается.

Публикация на Инфостарт

Комментариев нет:

Отправить комментарий