В практике разработки на платформе 1С:Предприятие 8.3 часто возникает дилемма: стоит ли обращаться к реквизитам документа через точку от ссылки или лучше написать запрос? На первый взгляд, обращение через точку выглядит проще и лаконичнее, однако с точки зрения производительности и потребления ресурсов системы разница может быть колоссальной. В этой статье мы подробно разберем, в каких ситуациях стоит использовать тот или иной метод, проанализируем механизмы работы платформы и выясним, как избежать типичных ошибок при работе с данными.
Представим типичную задачу: нам необходимо собрать данные заголовка документа (номер, дата, склад) и содержимое его табличной части (товары, количество) в структуру для последующей обработки или передачи. Рассмотрим пример кода, который часто встречается у начинающих разработчиков:
Данные = Новый Структура;
Данные.Вставить("Проведен", ОбъектОбработки.Проведен);
Данные.Вставить("ПометкаУдаления", ОбъектОбработки.ПометкаУдаления);
Данные.Вставить("Дата", ОбъектОбработки.Дата);
Данные.Вставить("Номер", ОбъектОбработки.Номер);
Данные.Вставить("Склад", ОбъектОбработки.Склад.ИДД);
Данные.Вставить("ИДД", XMLСтрока(ОбъектОбработки));
мсвТаблица = Новый Массив;
Для каждого текСтрока из ОбъектОбработки.Товары цикл
Товары = Новый Структура;
Товары.Вставить("Количество", текСтрока.Количество);
Товары.Вставить("Качество", текСтрока.Качество);
Товары.Вставить("ХарактеристикаНоменклатуры", текСтрока.ХарактеристикаНоменклатуры.ИДД);
мсвТаблица.Добавить(Товары);
КонецЦикла;
Чтобы понять, насколько эффективен этот код, нам нужно выяснить, чем является ОбъектОбработки. Рассмотрим два принципиально разных случая.
Если ОбъектОбработки — это непосредственно объект типа ДокументОбъект (например, полученный в модуле формы или через метод ПолучитьОбъект()), то данные уже считаны из базы данных и находятся в оперативной памяти сервера приложений. В этом случае обращение к его свойствам происходит практически мгновенно.
Рассмотрим особенности:
Объект.Склад.ИДД) все равно может инициировать дополнительные запросы к базе данных для получения данных связанных справочников.Ситуация кардинально меняется, если ОбъектОбработки — это ДокументСсылка. Рассмотрим подробнее, что происходит "под капотом" платформы при обращении к ссылке через точку.
Проанализируем механизм кэширования: при первом обращении к любому реквизиту ссылки (например, Ссылка.Дата), платформа считывает из базы данных всю строку таблицы заголовка документа. Это называется объектным кэшем. Если нам нужно всего два поля, а в документе их пятьдесят, мы заставляем систему передавать лишние данные и занимать лишнюю память.
Но главная проблема кроется в табличных частях. Обращение к Ссылка.Товары всегда инициирует отдельный запрос к базе данных. Если мы обрабатываем массив из 100 документов в цикле и обращаемся к их табличным частям через точку, мы получим 100 дополнительных запросов к СУБД. Это классическая проблема производительности "N+1".
Выясним причину, по которой опытные разработчики предпочитают использовать Запрос вместо прямого обращения к свойствам ссылки:
Количество, Качество), не нагружая память лишними данными.Выбрать() в запросе работает эффективнее, так как позволяет обходить данные последовательно, не загружая всю коллекцию в память целиком.Разобравшись в теории, посмотрим на пример реализации задачи через пакетный запрос. Это позволит нам получить данные шапки и табличной части максимально эффективно.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Проведен КАК Проведен,
| Док.ПометкаУдаления КАК ПометкаУдаления,
| Док.Дата КАК Дата,
| Док.Номер КАК Номер,
| Док.Склад.ИДД КАК СкладИДД,
| Док.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
| Док.Ссылка = &Ссылка;
|
|ВЫБРАТЬ
| Товары.Количество КАК Количество,
| Товары.Качество КАК Качество,
| Товары.ХарактеристикаНоменклатуры.ИДД КАК ХарактеристикаИДД
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
РезультатыПакет = Запрос.ВыполнитьПакет();
// Обработка данных шапки
ВыборкаШапка = РезультатыПакет[0].Выбрать();
Если ВыборкаШапка.Следующий() Тогда
Данные = Новый Структура;
ЗаполнитьЗначенияСвойств(Данные, ВыборкаШапка);
Данные.Вставить("ИДД", XMLСтрока(ВыборкаШапка.Ссылка));
КонецЕсли;
// Обработка табличной части
ВыборкаТЧ = РезультатыПакет[1].Выбрать();
мсвТаблица = Новый Массив;
Пока ВыборкаТЧ.Следующий() Цикл
СтрокаТЧ = Новый Структура("Количество, Качество, ХарактеристикаНоменклатуры");
ЗаполнитьЗначенияСвойств(СтрокаТЧ, ВыборкаТЧ);
СтрокаТЧ.ХарактеристикаНоменклатуры = ВыборкаТЧ.ХарактеристикаИДД;
мсвТаблица.Добавить(СтрокаТЧ);
КонецЦикла;
В данном примере мы использовали ВыполнитьПакет(), что позволило нам за одно обращение к серверу БД получить все необходимые данные. Обратите внимание на использование функции XMLСтрока(). Если вы работаете со ссылкой, передача ссылки в эту функцию — быстрая операция. Однако, если у вас уже есть объект, получение УникальныйИдентификатор() от ссылки объекта будет более правильным решением.
Проанализировав вышесказанное, выделим основные правила выбора метода получения данных:
Ссылка.Склад.Организация.Наименование) — используйте запрос, так как это позволит СУБД выполнить соединение таблиц оптимально.Ссылка.ИмяТабличнойЧасти.Соблюдение этих простых правил позволит вашим разработкам работать быстрее, потреблять меньше памяти и легче масштабироваться при росте объема данных в информационной базе.