gifts2017

Запросы &НаКлиенте для Управляемого приложения

Опубликовал Сергей Гершкович (sereginseregin) в раздел Программирование - Практика программирования

Разбитый между клиентом и сервером код размывает и усложняет логику прикладного решения. Для запроса из базы данных одной записи предлагаю &НаКлиенте использовать универсальную процедуру "ЗаполнитьИзЗапроса".

Процедура "ЗаполнитьИзЗапроса" сокращает и упрощает прикладной код:

  1. Запрос к базе данных составляется непосредственно в обработчике событий формы;
  2. Значения реквизитов формы процедура самостоятельно считывает в качестве параметров запроса;
  3. Значения реквизитов формы процедура самостоятельно заполняет результатом выборки данных.

По производительности процедура "ЗаполнитьИзЗапроса" медленнее традиционно реализованных процедур &НаСервереБезКонтекста, но быстрее, чем &НаСервере (с контекстом).

В обновленной публикации:

  • В процедуру "ЗаполнитьИзЗапроса" добавлена возможность указания имен параметров запроса в явном виде - это исключает дополнительный вызов сервера для автоматического определения перечня параметров Запроса;
  • Подправлен код;
  • Добавлена обработка для оценки производительности процедуры  "ЗаполнитьИзЗапроса".

Процедура ЗаполнитьИзЗапроса принимает четыре параметра:

  • ОбъектПриемник - куда копировать результат запроса;
  • ТекстЗапроса - стандартный параметризованный запрос;
  • ОбъектИсточник - откуда брать параметры для Запроса - необязательный параметр, по умолчанию используется ОбъектПриемник;
  • СтруктураПараметровЗапроса - необязательное перечисление параметров запроса и их значений. Исключает дополнительный вызов сервера для автоматического определения имен параметров в Запросе.

Пример заполнения на форме “КурсаВалюты” &НаКлиенте через предлагаемую процедуру:

Порядок работы процедуры ЗаполнитьИзЗапроса:

  1. Если СтруктураПараметровЗапроса не указана явно, параметры определяются в ТекстеЗапроса автоматически  &НаСервереБезКонтекста;
  2. &НаКлиенте СтруктураПараметровЗапроса заполняется значениями свойств ОбъектаИсточника (символ “_” (подчеркивание) в имени параметра запроса интерпретируется как “.” (точка) для доступа к вложенным свойствам объекта);
  3. &НаСервереБезКонтекста выполняется ТекстЗапроса;
  4. &НаКлиенте заполняются свойства ОбъектаПриемника значениями полей результирующей таблицы Запроса (символ “_” (подчеркивание) в имени поля интерпретируется как “.” (точка) для доступа к вложенным свойствам объекта).

Оценка производительности для толстого клиента:

  • Процедура ЗаполнитьИзЗапроса (п.2.2.) выполняется в 1,5 раза медленнее традиционно реализованной функции на &НаСервереБезКонтекста (п.1.3.), но в 2 раза быстрее процедуры на &НаСервере (с контекстом) (п.1.4.);
  • Автоматическое определение параметров запроса увеличивает время выполнение процедуры ЗаполнитьИзЗапроса в 1,7 раза  (п.2.3.). Данную опцию удобно использовать на этапе отладки, когда параметры запроса окончательно не определены.
  • ЛЮБОПЫТНО! Вызов клиентом сервера без контекста (п.1.3.) в 3!!! раза медленнее обращения сервера к БД (п.2.1.).

Ниже представлен код реализованных процедур:

&НаКлиентеНаСервереБезКонтекста
Процедура ЗаполнитьИзЗапроса(ОбъектПриемник, Знач ТекстЗапроса, Знач ОбъектИсточник=Неопределено, Знач СтруктураПараметровЗапроса=Неопределено)  
	ОбъектИсточник = ?(ОбъектИсточник=Неопределено,ОбъектПриемник,ОбъектИсточник);
	
	Если ТипЗнч(СтруктураПараметровЗапроса)<>Тип("Структура") тогда
		СтруктураПараметровЗапроса = Новый Структура;
		
		Для каждого ИмяПараметра Из ИменаПараметровЗапроса(ТекстЗапроса) Цикл
			СтруктураПараметровЗапроса.Вставить(ИмяПараметра);
		КонецЦикла;         
	КонецЕсли;
	 
		
	Для каждого Параметр Из СтруктураПараметровЗапроса Цикл
		Если Параметр.Значение=Неопределено тогда
			Попытка
				СтруктураПараметровЗапроса.Вставить(Параметр.Ключ, ПрочитатьВложенныйОбъект(ОбъектИсточник, Параметр.Ключ));
			Исключение
				СтруктураПараметровЗапроса.Вставить(Параметр.Ключ, Null);
					
			КонецПопытки;
		КонецЕсли;
	КонецЦикла;         
	
	МассивЗначений = ЗаполнитьМассивРезультатомЗапроса(СтруктураПараметровЗапроса, ТекстЗапроса, 1);
	
	Если МассивЗначений.Количество()>0 тогда
		Для каждого Колонка Из МассивЗначений[0] Цикл
			Попытка
				ЗаписатьВоВложенныйОбъект(Колонка.Значение,  ОбъектПриемник, Колонка.Ключ)	
			Исключение
				
			КонецПопытки;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

&НаКлиентеНаСервереБезКонтекста
Функция ПрочитатьВложенныйОбъект(Объект, Путь="", Разделитель="_")
	ПозицияРазделителя = Найти(Путь, Разделитель);
	
	Если ПозицияРазделителя=0 тогда
		ПозицияРазделителя=СтрДлина(Путь)+1;
	КонецЕсли;	
	
	ИмяВложенногоОбъекта=Сред(Путь,1,ПозицияРазделителя-1);
	ОставшийсяПуть=Сред(Путь,ПозицияРазделителя+1);
	
	Если ИмяВложенногоОбъекта="" тогда
		Возврат Объект;
	КонецЕсли;
	
	Если ОставшийсяПуть="" тогда
		Возврат Объект[ИмяВложенногоОбъекта];
	КонецЕсли;
	
	Возврат ПрочитатьВложенныйОбъект(Объект[ИмяВложенногоОбъекта], ОставшийсяПуть, Разделитель);	
КонецФункции

&НаКлиентеНаСервереБезКонтекста
Процедура ЗаписатьВоВложенныйОбъект(Значение, Объект, Путь="", Разделитель="_")
	ПозицияРазделителя = Найти(Путь, Разделитель);
	
	Если ПозицияРазделителя=0 тогда
		ПозицияРазделителя=СтрДлина(Путь)+1;
	КонецЕсли;	
	
	ИмяВложенногоОбъекта=Сред(Путь,1,ПозицияРазделителя-1);
	ОставшийсяПуть=Сред(Путь,ПозицияРазделителя+1);
	
	Если ИмяВложенногоОбъекта="" тогда
		Объект=Значение;
		Возврат;
	КонецЕсли;
	
	Если ОставшийсяПуть="" тогда
		Объект[ИмяВложенногоОбъекта]=Значение;
		Возврат;
	КонецЕсли;
	
	ЗаписатьВоВложенныйОбъект(Значение, Объект[ИмяВложенногоОбъекта], ОставшийсяПуть, Разделитель);	
КонецПроцедуры

&НаСервереБезКонтекста
Функция ИменаПараметровЗапроса(ТекстЗапроса)  
	Запрос = Новый Запрос(ТекстЗапроса);
	
	МассивПараметров = Новый Массив;
	
	Для каждого Параметр Из Запрос.НайтиПараметры() Цикл
		  МассивПараметров.Добавить(Параметр.Имя)
	КонецЦикла;
	
	Возврат МассивПараметров;
КонецФункции

&НаСервереБезКонтекста
Функция ЗаполнитьМассивРезультатомЗапроса(СтруктураПараметров, ТекстЗапроса, КоличествоЗаписей=0) 
	Запрос = Новый Запрос(ТекстЗапроса);
	
	Для каждого Параметр Из СтруктураПараметров Цикл
		  Запрос.УстановитьПараметр(Параметр.Ключ, Параметр.Значение);
	КонецЦикла;
	
	РезультатЗапроса = Запрос.Выполнить();
	
	МассивИменКолонок = Новый Массив;
	Для каждого Колонка Из РезультатЗапроса.Колонки Цикл
		МассивИменКолонок.Добавить(Колонка.Имя);
	КонецЦикла;
	
	ВыборкаЗаписей = РезультатЗапроса.Выбрать();
	РезультирующийМассив = Новый Массив();
	
	Если ВыборкаЗаписей.Следующий() тогда
		СтруктураДанных = Новый Структура();
		
		Для каждого ИмяКолонки Из МассивИменКолонок Цикл
			СтруктураДанных.Вставить(ИмяКолонки, ВыборкаЗаписей[ИмяКолонки]);
		КонецЦикла;
		
		РезультирующийМассив.Добавить(СтруктураДанных);
		
		Если КоличествоЗаписей<>1 тогда
			ВыбраноЗаписей=1;
			
			Пока ВыборкаЗаписей.Следующий() и (ВыбраноЗаписей<КоличествоЗаписей или КоличествоЗаписей=0) цикл
				СтруктураДанных = Новый Структура();
				
				Для каждого ИмяКолонки Из МассивИменКолонок цикл
					СтруктураДанных.Вставить(ИмяКолонки, ВыборкаЗаписей[ИмяКолонки]);
				КонецЦикла;
				
				РезультирующийМассив.Добавить(СтруктураДанных);
				
				ВыбраноЗаписей=ВыбраноЗаписей+1;
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;

	Возврат РезультирующийМассив;
КонецФункции


Скачать файлы

Наименование Файл Версия Размер Кол. Скачив.
Пример работы с процедурой "ЗаполнитьИзЗапроса"
.epf 9,06Kb
07.08.16
2
.epf 2 9,06Kb 2 Скачать
Тест производительности
.epf 10,25Kb
07.08.16
0
.epf 10,25Kb 0 Скачать

См. также

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

Комментарии

1. Павел Чистов (GROOVY) 08.05.16 17:39
Шикарно. Сериализовать весь результат любого запроса и передавать его на клиента. Прям слов нет.
theshadowco; DrAku1a; kuzyara; 1С_Мастер; PrinzOfMunchen; cleaner_it; +6 Ответить 1
2. Дмитрий Шерстобитов (DitriX) 09.05.16 18:22
Это может потребоваться только в той конфигурации - где результат запроса ВСЕГДА будет разный, в противном случае - достаточно использовать общие модули с галокой - повторного использования, иначе, при более менее средних объмах - рискуете убить нафиг все :)
Ну и второе - это что, всегда при получении курса писать запрос? :)
Вобщем я бы на вашем месте скрыл эту статью :)
theshadowco; DrAku1a; GROOVY; awk; +4 Ответить 1
3. Сергей Гершкович (sereginseregin) 09.05.16 18:30
(1) GROOVY,
Сериализовать весь результат любого запроса и передавать его на клиента

  • Данных передается не больше, чем необходимо &НаКлиенте в каждом конкретном случае. (...немного схалтурил-недоделал с "КоличествоСтрок=0", но) Какой ...разработчик определяет в запросах выборку лишних полей или строк.
  • Когда разработчиков >3, форм >100, а заказчик требует "реактивного" решения, на первый план выходит простота и наглядность прикладного кода.
  • Подобное применение запросов &НаКлиенте переводит код из императивного ближе в декларативный.

Шикарно... Прям слов нет.
- конструктивно!


4. Сергей Гершкович (sereginseregin) 09.05.16 19:22
(2) DitriX,
Это может потребоваться только в той конфигурации - где результат запроса ВСЕГДА будет разный,

... не понял, как Вы предугадываете одинаковый результат запроса
в противном случае - достаточно использовать общие модули с галокой - повторного использования

надеялся понятно, процедуры надо раскидать по общим модулям, или Вы о другом?
Ну и второе - это что, всегда при получении курса писать запрос? :)

Я за здоровый компромисс, в простейших формах используйте общую функцию КурсВалюты,
Бывают ситуации, значения валюты или даты не находятся на форме, а вычисляются по ссылке с замороченными условиями, поэтому вычисление этих условий напрямую в запросе курса валюты может выглядеть нагляднее, но это ... Вам решать.
Вобщем я бы на вашем месте скрыл эту статью :)

Эх... Рискну!
5. Дмитрий Шерстобитов (DitriX) 09.05.16 19:44
Я к тому, что не зря их разносят по модулям, почитайте про то, что такое модуль повторного использования.
И тогда сразу станет понятно - почему описаный вами механиз не просто узкоспециализированный, а по сути и бесполезный.
Т.е. вы сами нарисовали сферического коня в вакууме, и говорите о том, что он реальный. Не, ну никто же не спорит, почему же этому механизму то не работать?
Но вот если я при найме на работу, или при приеме самой работы - увидел бы такое решение, я бы послал передлывать, и не за-за того, что я такой злой и не понимаю, а наоборот.

То что вы описали - к этому народ тянулся еще лет 6 назад, и каждый второй придумывал такие костыли (и я там же был, чего уж там), но, их оставили в прошлом, и не зря.

Почему прошу скрыть - не учите плохому, не надо такое видеть неокрепшим умам, а то вы только хуже сделаете людям.
Вместо этого - описали бы минусы такого подхода, и рассказали - почему так делать нельзя, и это бы было бы и для вас интересней, и для новичков, тоже было бы весьма интересно. Та чего уж скрывать - и мне тоже :)
6. Сергей Гершкович (sereginseregin) 09.05.16 22:35
(5) DitriX,
...модуль повторного использования

Объясните начинающему, в чем проблемы (бесполезность) с повторным использованием: нагрузка на сервер из-за 4-х вызовов функций или кэширование непредсказуемо.

То что вы описали - к этому народ тянулся еще лет 6 назад

К сожалению, кроме жалоб бывалых и бравады авангардистов ничего конструктивного не нашел

не учите плохому, не надо такое видеть неокрепшим умам
Ваше мнение поможет этим умам критичнее подойти к предлагаемому решению

Вместо этого - описали бы минусы такого подхода
Много лет, НО не в 1С, успешно использую данный подход с классическим SQL. Вычисления выполняются на СУБД - проблем со нагрузкой никаких. Клиент только для отображения данных.

Решил таки (перед пенсией:-) освоить плюшки и кружева 1С. Лучший способ обучения - показать свое решение, получить отзыв. Понимаю, полез в чужой монастырь со своим уставом, но в чем религии у нас разные, все равно, пока не понял.
7. al petrov (petrov_al) 10.05.16 12:03
По моему решение очень оригинальное и применимо. А то что автор не знает в чем особенность модуля повторного использования ...это упустим.
8. Пикалев Николай (dabu-dabu) 10.05.16 12:17
Оригинального особо ничего нет. Очередная попытка сделать универсальную функцию которая облегчит жизнь. Как показывает практика долго такие штуки не живут, т.к. в конечном итоге обрастают кучей функционала и превращаются в не поддерживаемых медленных монстров.

Ошибки в коде:
1. У функции "ЗаполнитьИзЗапроса" 2 вызова сервера: ИменаПараметровЗапроса, ЗаполнитьМассивРезультатомЗапроса. Причем от задвоенности совершенно легко избавиться. За такие штуки минимум - лишать премии.
2. У функции ЗаполнитьМассивРезультатомЗапроса есть безобразная строчка "СтруктураДанных = Новый Структура(МассивИменКолонок)".

Допущенные ошибки говорят о том что вы как программист достаточно слабый и малоопытный.
Соответственно, лучше бросайте заниматься фигней такой как "разработка универсальных функций облегчающих жизнь". То что они облегчают жизнь - это только кажется на первый взгляд.
9. Сергей Гершкович (sereginseregin) 10.05.16 13:43
(8) dabu-dabu,
Как показывает практика ... превращаются в не поддерживаемых медленных монстров

Это выжимка из долгоиграющего монстра (не 1С), которая показала свои преимущества как простая схема для конфигурации вычислений на большом объеме форм.

Вам непривычно писать запросы? А у меня после тысячи написанных SQL рука не поднимается обрабатывать данные циклами - слишком много вспомогательных переменных, и индексы не работают. Я вообще перестал понимать, для чего использовать циклы в базах данных, когда все операции работают через SQL, но это пока не про 1C.

В 1С выглядит как костыль? Согласен, помогите, как это в 1С будет работать правильней.

1С меня заинтересовал как платформа для создания прототипа бизнес решения, которое рискует вызвать еще больше возмущения своей непосредственностью ;-). Для реализации не хватает нескольких тех.решений. С рекурсивным запросом я решил (отзывов не получил). С запросами на клиенте показываю, будем дорабатывать. Возможно, еще понадобится генератор прав доступа на уровне записей, но это по мере надобности.


У функции "ЗаполнитьИзЗапроса" 2 вызова сервера: ИменаПараметровЗапроса, ЗаполнитьМассивРезультатомЗапроса. Причем от задвоенности совершенно легко избавиться.

Если обе функции находятся в общем модуле, ничего не знают о контексте формы, как Вы собираетесь их объединить без 2-го пункта:
1. На сервере стандартной функцией определяются имена параметров Запроса;
2. На клиенте заполняются параметры Запроса значениями реквизитов формы;
3. На сервере выполняется Запрос.

У функции ЗаполнитьМассивРезультатомЗапроса есть безобразная строчка "СтруктураДанных = Новый Структура(МассивИменКолонок)"

Согласен, МассивИменКолонок тут лишний. Видимо осталось, когда искал варианты, как быстрее выборку засунуть в массив.

Постараюсь подправить эту опечатку, и еще кое что в ближайшее время...


То что они облегчают жизнь - это только кажется на первый взгляд.


Программист пишет - зарплата капает!
10. Пикалев Николай (dabu-dabu) 10.05.16 16:14
(9) sereginseregin,
1. При чем здесь
выжимка из долгоиграющего монстра (не 1С)
когда пример именно про платформу 1С и при чем здесь
Вам непривычно писать запросы?
Осталю без комментариев.


А ваше
Возможно, еще понадобится генератор прав доступа на уровне записей, но это по мере надобности.
как раз и показывает то о чем я говорю:
долго такие штуки не живут, т.к. в конечном итоге обрастают кучей функционала и превращаются в не поддерживаемых медленных монстров


2.
как Вы собираетесь их объединить без 2-го пункта
Могу посоветовать только учится программировать.

3.
Программист пишет - зарплата капает!
Т.е. ценность вашей деятельности в потраченном времени? Не завидую.
11. Сергей Гершкович (sereginseregin) 10.05.16 17:28
(9) dabu-dabu,
[IS-QUOTE]Программист пишет - зарплата капает!

Т.е. ценность вашей деятельности в потраченном времени? Не завидую.[/IS-QUOTE]
Э-эх! Знак вопроса после "капает" не поставил - теперь сам виноват.

как раз и показывает то о чем я говорю:
долго такие штуки не живут

Занимаюсь я подобными проектами в свободное от работы время. Какие-то идеи удается внедрить в рабочих проектах. Какие-то естественно отмирают. Условно удачных 60 к 40 неудачным, и это нормально. Если у Вас наоборот, не экспериментируйте.


Могу посоветовать только учится программировать.

Диалог пришел в тупик.
12. Игорь Мирошниченко (igormiro) 10.05.16 17:39
Ничего не обычного в статье я не нашел, а статью надо переименовать, передача параметров запроса с сервера на клиент.
13. Андрей Акулов (DrAku1a) 12.05.16 02:41
Для решения мелких задач - встречается:
&НаСервере
Процедура ВыполнитьКод(Код, Параметр=неопределено)
  Выполнить(Код);
КонецПроцедуры

&НаСервере
Функция ВычислитьКод(Код, Параметр=неопределено)
  Возврат Вычислить(Код);
КонецФункции

&НаСервере
Функция ВыполнитьЗапрос(ТекстЗапроса, Параметры=неопределено, КодОбработкиРезультата=неопределено)
  Запрос = новый Запрос(ТекстЗапроса);
  Если ТипЗнч(Параметры)=Тип("Структура") Тогда
    Для каждого П из Параметры Цикл Запрос.УстановитьПараметр(П.Ключ, П.Значение); КонецЦикла;
  КонецЕсли; 
  Результат = Запрос.Выполнить();
  Если КодОбработкиРезультата=неопределено Тогда
    Результат = Результат.Выгрузить().ВыгрузитьКолонку( 0 );
  Иначе
    Вычислить(КодОбработкиРезультата);
  КонецЕсли;
  Возврат Результат;
КонецФункции
...Показать Скрыть
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа