gifts2017

Дополнительные возможности параметров, передаваемых в запросе

Опубликовал Дмитрий Лазаренко (SCRIPT91) в раздел Программирование - Практика программирования

Как из переданного параметра ссылочного типа в запросе получить различные поля объекта конфигурации

На днях писал запрос и наткнулся на интересную особенность при использовании параметров. Если передать параметры ссылочных типов, то к ним можно обращаться как к объектам конфигурации -  через оператор "точка". Например: нам необходимо выбрать документы "исполнительные листы" по какой-либо организации. Причем если в качестве параметра передается организация с кодом "01", нам нужно выбрать документы относящееся к организациям с кодами "01", "02", "03", иначе выбрать документы по переданной организации:

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
|ИсполнительныйЛист.Организация КАК Организация,
|ИсполнительныйЛист.Физлицо КАК Сотрудник,
|ИсполнительныйЛист.Предел КАК СуммаВсего
| 
|ИЗ
|Документ.ИсполнительныйЛист КАК ИсполнительныйЛист
|			
|ГДЕ
|ВЫБОР КОГДА (ВЫРАЗИТЬ((&Организация).Код КАК СТРОКА(2))= "01" )
|	 ТОГДА ВЫРАЗИТЬ(ИсполнительныйЛист.Организация.Код КАК СТРОКА(2)) В ("01","02","53")
|	 ИНАЧЕ
|    	 ИсполнительныйЛист.Организация = &Организация
|КОНЕЦ";
Запрос.УстановитьПараметр("Организация", Справочник.Организации.НайтиПоКоду("01"));
 

В этом запросе мы обращаемся к параметру ссылочного типа "&Организация", мы можем извлечь из этого параметра любую информацию, например: наименование (&Организация.Наименование) или ИНН (&Организация.ИНН), словом все те реквизиты, которые есть в справочнике "Организации". При попытке открыть этот запрос в конструкторе запросов будет выдаваться ошибка, т.к. конструктор считает, что к параметру нельзя так обращаться, но на самом деле запрос РАБОТАЕТ!

 

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Иван Лазаренко (GH0STexe) 27.06.14 06:14
Норм. Не знал,что так можно. Типа недокументированная фича что ли ?
2. TMV 27.06.14 08:42
(0), Интересная находка, но все же предпочитаю, чтобы конструктор запроса не выдавал ошибок о невозможности чтения текста запроса.
3. Тимофей Шантин (ShantinTD) 27.06.14 10:15
О_о. Прикольно. И иногда (насколько часто?) полезно. Сам тоже предпочитаю конструкторопригодный запрос, так что согласен с (2). Но на заметку возьму!
4. Владимир Кузнецов (mr.Kot) 27.06.14 11:38
Интересная вещь, не знаю, запомню ли, т.к. для применения они вряд ли будет часто нужна. Но применять может, к сожалению, только в простых запросах, которые легко читать глазами и вручную (без конструктора) исправлять, что, кстати, для программистом с ещё не большим опытом тяжело.
5. Тимофей Шантин (ShantinTD) 30.06.14 09:12
Для возможности редактирования запроса с применением такого приема могу предложить комбинировать его с приемом замены текста.
Поясню: в приведенном в (0) запросе можно написать &Организация_Код, и запрос можно будет открыть редактором. Если произвести в тексте запроса замену (самым простым СтрЗаменить(...)) на &Организация.Код, то получим запрос "с хитростью". А можно и не подменять ничего, а просто передать в запрос несколько "лишних" параметров. Для новичков это должно быть проще, чем редактировать запрос "руками".
А если запрос уже отлажен, "вылизан", и редактировать его больше не предполагается, то можно смело заменить непосредственно в тексте запроса "лишние" параметры так, как предложено в (0).
loy; bird21; SCRIPT91; +3 Ответить 1
6. Яков Коган (Yashazz) 30.06.14 13:12
(2) Конструктор иногда сам, например, сгенерит текст, и сам же его открыть не может. Так что в (5) правильно сказано - всякими строковыми играми можно его обмануть и выкрутиться.
7. Антон Стеклов (asved.ru) 02.07.14 04:12
Сама по себе идея отбора по реквизиту "через точку" - лишнее соединение. Причем справочник "Организации", как правило, невелик, а значит, читаться будет scan'ом.

В то же время когда мы передаем в параметр массив ссылок, соединения с таблицей справочника Организации не будет вообще.
AlexanderKai; +1 Ответить
8. danila_zlt (danila_zlt) 02.07.14 06:34
ВЫРАЗИТЬ(&Организация как Справочник.Организации).Код

И конструктор не будет ругаться.
boln; Nuobu; the1; Рамзес; echo77; Puk2; loy; wolfsoft; BigB; serg_gres; Yimaida; bird21; ShantinTD; vde69; +14 Ответить 3
9. Александр (МимохожийОднако) 02.07.14 07:03
Кто-нибудь делал замер подобной конструкции по сравнению со стандартным запросом? Подход любопытный, но как это влияет на производительность и какой результирующий запрос будет к SQL-серверу?
10. Дмитрий Воробьев (vde69) 02.07.14 07:42
рекомендую использовать (8) так как (0) грозит запросом к метаданным строк этак на 500.

Вообще любые нетипизированые вещи в запросе - зло, именно по этому рекомендуется использовать типизированые ТЗ в качестве параметров.
anchovy; Ish_2; Dach; ShantinTD; Makushimo; +5 Ответить
11. Семён Павлюков (7OH) 02.07.14 13:04
Хм.
А не легче ли пользоваться левым соединением, чем писать нечитаемые запросы ?
Ну или
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
|ИсполнительныйЛист.Организация КАК Организация,
|ИсполнительныйЛист.Физлицо КАК Сотрудник,
|ИсполнительныйЛист.Предел КАК СуммаВсего
| 
|ИЗ
|Документ.ИсполнительныйЛист КАК ИсполнительныйЛист
|            
|ГДЕ
|ВЫБОР КОГДА (&другаяОрганизация = &Организация01 )
|     ТОГДА ВЫРАЗИТЬ(ИсполнительныйЛист.Организация.Код КАК СТРОКА(2)) В ("01","02","53")
|     ИНАЧЕ
|         ИсполнительныйЛист.Организация = &Организация
|КОНЕЦ";
Запрос.УстановитьПараметр("Организация01", Справочник.Организации.НайтиПоКоду("01"));
Запрос.УстановитьПараметр("Организация", другаяОрганизация);
...Показать Скрыть
12. Тимофей Шантин (ShantinTD) 02.07.14 14:11
(11) 7OH, Какова судьба параметра &другаяОрганизация ?

Еще пример: есть справочник, у него в реквизитах хранятся 2 даты - начало и конец периода (за который предполагается провести некую оценку). Так вот предложенный в (8) приём позволит передать всего один параметр, и спокойно работать с его реквизитами.
Выбрать 
 *
ИЗ 
 РегистрНакопления.ПродажиПоДисконтнымКартам КАК ПродажиПоДисконтнымКартам
ГДЕ
 ПродажиПоДисконтнымКартам.Период МЕЖДУ ВЫРАЗИТЬ(&ПризнакРассылки КАК Справочник.ПризнакСМСРассылки).НачалоПродаж И ВЫРАЗИТЬ(&ПризнакРассылки КАК Справочник.ПризнакСМСРассылки).КонецПродаж
...Показать Скрыть


Использование лишней временной таблицы, соединение, и все сопутствующие навороты навряд ли добавит читаемости запросу, и, сдается мне, негативно скажется на времени выполнения запроса.

А по поводу Вашего "ну или": в (0) вопрос ставился именно о том, чтобы передавать параметр и обращаться к его реквизитам. В (8) было предложено как при этом сделать запрос пригодным для редактирования конструктором (да и на глаз он сильно страдает). В предложенном Вами варианте нет обращения к реквизитам параметра. В чем "дополнительная возможность использования параметра"? В варианте с "левым соединением" аналогично не будет обращения к реквизитам параметра, да еще и усложнение запроса убавит его читаемости и добавит узких мест (в том смысле, что на лишнем соединении больше шансов накосячить). Кстати: если в том месте, где появится это самое "лишнее" соединение и без того уже есть соединение (нужное, не лишнее), то вероятность накосячить в соединениях многократно возрастает. А обращение к реквизитам параметра позволит спокойно описать все нужные условия или связи.

Хотя могу и заступиться за Ваш способ с соединением: реквизиты параметра (из которого получается таблица из целой одной записи) доступны конструктором. При простом обращении к реквизитам параметра придется писать реквизиты руками, а конструктор будет лишь ругаться, что не нашел такого при неправильном написании, не предлагая правильных вариантов.
13. Евгений Сивов (bird21) 02.07.14 14:35
Любопытно, не сталкивался раньше с таким.
14. Damian (Damian) 04.07.14 09:08
Книга "Язык запросов 1С:Предприятие 8" от Хрусталевой и Радченко (2013 год) в разделе про оптимизацию запросов настоятельно рекомендует воздержаться от применения каких-либо функций к передаваемым в запрос параметрам. Лучше все необходимое вычислить снаружи и передать в запрос уже константы.
Даже банальное "НАЧАЛОПЕРИОДА(&ДатаОстатков,ДЕНЬ)" не советуют использовать в тексте запроса.
15. Никита Коротаев (bforce) 04.07.14 10:54
По хорошему такие запросы нужно писать так.

Способ 1. Получаем нужные данные в коде, а не в запросе.
|ВЫБОР
| КОГДА &Организация = &ОсобаяОрганизация
| ТОГДА ИсполнительныйЛист.Организация В (&МассивОгранизаций)
| ИНАЧЕ
| ИсполнительныйЛист.Организация = &Организация
|КОНЕЦ

Запрос.УстановитьПараметр("Организация", Документ.Огранизация);
Запрос.УстановитьПараметр("ОсобаяОрганизация", Справочник.Организации.НайтиПоКоду("01")); // А лучше здесь завести константу или обращаться к регистру сведений.
Запрос.УстановитьПараметр("МассивОгранизаций", СписокОгранизаций);

Способ 2. Избавляемся от сложной конструкции в условии ГДЕ (для СУБД так будет легче).

|ГДЕ
| ИсполнительныйЛист.Организация В (&МассивОгранизаций)

Запрос.УстановитьПараметр("МассивОгранизаций", УжеПодготовленныйСписокВашихОрганизаций);

Способ 3. Для тех, случаев, когда избежать обращения через точку не удается.

|ВЫБОР
| КОГДА
| ВЫРАЗИТЬ (&Организация КАК Справочник.Организации).ИНН ПОДОБНО "68465_"
|КОНЕЦ

Хотя, я, наверное, излишне помешан на оптимизации. Работа такая. Приношу извинения.
16. Антон Стеклов (asved.ru) 04.07.14 11:48
bforce,

| ВЫРАЗИТЬ (&Организация КАК Справочник.Организации).ИНН ПОДОБНО "68465_"(15)


Применение ВЫРАЗИТЬ здесь никакой смысловой нагрузки не несет, т.к. платформа определяет тип параметра, и он у нас в данном случае простой.

ВЫРАЗИТЬ применяется к полю составного типа для ограничения типов (а следовательно, количества соединений) данных, которые требуют обращения через точку.

Грубо говоря, если у нас есть поле составного типа Регистратор {ДокументСсылка.Реализация, ДокументСсылка.Поступление}
и мы пишем "ВЫБРАТЬ Регистратор.Дата" то запрос будет состоять из двух соединений:
1) Наша исходная таблица с таблицей Документ.Поступление по равенству поля Ссылка
1) Наша исходная таблица с таблицей Документ.Реализация по равенству поля Ссылка

А если мы напишем "ВЫБРАТЬ ВЫРАЗИТЬ(Регистратор КАК Документ.Реализация).Дата",
то соединение будет только одно:
Наша исходная таблица с таблицей Документ.Реализация по равенству поля Ссылка

второе соединение будет отфильтровано именно применением оператора ВЫРАЗИТЬ
17. Антон Стеклов (asved.ru) 04.07.14 11:50
(14) Damian, это общая рекомендация. На самом деле так делать можно, если понимаешь, что делаешь и сколько раз будет выполнено вычисление значения.
18. Никита Коротаев (bforce) 04.07.14 12:13
(16), ВЫРАЗИТЬ здесь имеет смысл потому, что я привожу параметр к определенному типу и гарантированно не получу ошибки в запросе при обращении к реквизиту объекта. Кроме того, запрос в конструкторе нормально открывается (автору это, кажется, было важно).
Если вы предпочитаете такую конструкцию
ВЫРАЗИТЬ((&Организация).Код КАК СТРОКА(2)
,
то должны в полной мере понимать что это за собой может повлечь.

Про составной тип я не стал грузить, хотя в этом случае приведение к типу для получения значения реквизита стоит использовать почти всегда.

(14), и правильно пишут, причем? не только они. Здесь, под заголовком "Эффективные планы запросов", как раз такой пример и приводится. Хотя, кого это интересует? Лучше ж "на коленке" написать и не думать о последствиях! Если у разработчика база с тремя пользователями, то такое, конечно же, прокатит, и он не заметит разницы.
19. Павел Алексеенко (qwinter) 09.07.14 11:00
(12) ShantinTD, учитывайте, что при каждом обращении через точку, будет еще одна (и одну ли?) временная таблица.
Использование лишней временной таблицы, соединение, и все сопутствующие навороты навряд ли добавит читаемости запросу, и, сдается мне, негативно скажется на времени выполнения запроса.
использование временной таблице скажется хорошо на времени выполнения запроса. Как минимум 1С не придется самой дописывать в запрос эту временную таблицу (а она любит несколько подзапросов строить, для вытаскивания всех данных). К тому же эти подзапросы будут построены на каждое такое обращение.
Еще пример: есть справочник, у него в реквизитах хранятся 2 даты - начало и конец периода (за который предполагается провести некую оценку). Так вот предложенный в (8) приём позволит передать всего один параметр, и спокойно работать с его реквизитами.
Только передаваемый параметр у вас и так в кеше есть, и его реквизиты уже прочитаны в тот момент, когда вы его передаете.
20. Евгений Фербер (omut) 15.07.14 01:54
Интересный момент. По кэшу: на сколько помню, получение ссылки не означает чтение реквизитов. В связи с этим, нужно смотреть контекст выполнения запроса. Если до выполнения реквизиты прочитаны, то смысла изгаляться подобным образом нет. Вред один. А вот если не прочитаны, то потенциально можно получить даже увеличение производительности. Поправьте, если не прав.
21. Павел Алексеенко (qwinter) 15.07.14 09:12
(20) omut, насколько помню при получении ссылки в кеш сразу считываются кэшируемые показатели, будем считать, что эти два не кэшируемые. В этом случае в кеш будет считаны все реквизиты ссылки при обращении к первому реквизиту (максимум один запрос в любом случаее). Второй будет считан из кеша. При передачи одного параметра в запрос, при каждом обращении через точку будет добавлен подзапрос. То минимум будет два дополнительных запроса (на каждый реквизит) + столько запросов, сколько раз вы к этим реквизитам обратитесь в запросе.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа