Вопрос о производительности между использованием объекта Запрос и метода Прочитать() объекта НаборЗаписей при работе с регистрами сведений является одним из наиболее часто задаваемых в сообществе 1С. Мы с вами детально разберем этот аспект, выясним, какой подход предпочтительнее для простой выборки данных, и проанализируем причины такого выбора.
Если мы говорим исключительно о чтении данных из регистра сведений без их последующей модификации, то, согласно официальным рекомендациям 1С, опубликованным на ИТС, Запрос будет быстрее в большинстве случаев.
Мы рассмотрим, почему использование объекта Запрос и его метода Выполнить() является предпочтительным для простых выборок из регистра сведений, когда нам не требуется изменять данные. Гибкость и эффективность запросов делают их мощным инструментом для извлечения информации из базы данных.
Преимущества использования объекта Запрос:
Запрос позволяет напрямую формировать SQL-запросы к системе управления базами данных (СУБД), что минимизирует промежуточные слои обработки платформой 1С. Это означает, что СУБД выполняет работу по выборке данных максимально эффективно, используя свои внутренние оптимизаторы и индексы.Запрос и последующей обработке данных через объект ВыборкаИзРезультатаЗапроса, платформа не загружает все данные в оперативную память сразу в виде объектов 1С. Выборка происходит построчно (или порциями), что значительно снижает потребление оперативной памяти, особенно при работе с большими объемами данных. Мы не создаем избыточных объектов ЗаписьРегистраСведений, которые не будут модифицироваться.СрезПоследних, СрезПервых), агрегатные функции, что делает запросы универсальным инструментом для аналитической работы.Пример использования запроса для чтения данных:
Предположим, нам нужно получить все записи регистра сведений "Документооборот" для определенной доставки и этапа.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документооборот.ДатаВремя,
| Документооборот.Отдел,
| Документооборот.ФИО,
| Документооборот.Документы,
| Документооборот.Примечание
|ИЗ
| РегистрСведений.Документооборот КАК Документооборот
|ГДЕ
| Документооборот.Доставка = &Доставка
| И Документооборот.Этап = &Этап";
Запрос.УстановитьПараметр("Доставка", Доставка); // Переменная Доставка должна быть определена
Запрос.УстановитьПараметр("Этап", Этап); // Переменная Этап должна быть определена
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Мы можем работать с данными выборки
Сообщить("ДатаВремя: " + Выборка.ДатаВремя + ", Отдел: " + Выборка.Отдел);
// ... и т.д.
КонецЦикла;
В этом примере мы создаем Запрос, устанавливаем параметры, выполняем его и затем построчно обходим Выборку. Данные извлекаются по мере необходимости, без предварительной полной загрузки в память.
Теперь мы обратимся к объекту НаборЗаписей и выясним, когда его использование оправдано, а когда нет, особенно в контексте производительности.
Объект НаборЗаписей (например, РегистрыСведений.ДокументоОборот.СоздатьНаборЗаписей()) в первую очередь предназначен для модификации (изменения, записи и удаления) данных регистра сведений. Его механизм работы отличается от прямого запроса:
Прочитать() для объекта НаборЗаписей, платформа сначала выполняет внутренний запрос к базе данных, затем загружает все полученные данные и создает коллекцию объектов типа "ЗаписьРегистраСведений" в оперативной памяти. Каждый такой объект является полноценным объектом 1С со всеми его свойствами и методами.НаборЗаписей относительно ограничен. Мы можем устанавливать условия равенства только для измерений регистра, и эти условия связываются логической операцией "И". Более сложные условия, виртуальные таблицы или агрегатные функции недоступны напрямую.Когда уместно использовать НаборЗаписей:
Мы используем НаборЗаписей, когда нашей основной целью является изменение или запись данных в регистр сведений. Это его прямое и наиболее эффективное назначение:
НаборЗаписей с помощью метода Загрузить(), а затем пакетно записать изменения. Это значительно производительнее, чем записывать каждую запись по отдельности в цикле.Записать() для пустого набора записей, мы можем удалить все записи, соответствующие отбору.Пример использования НаборЗаписей.Прочитать() (и его модификации) из сообщения на форуме:
Обратите внимание, что даже в примере из сообщения 5, после Прочитать(), записи модифицируются, а затем вызывается НаборЗаписей.Записать(). Это подтверждает, что НаборЗаписей наиболее эффективен для сценариев изменения данных.
НаборЗаписей = РегистрыСведений.ДокументоОборот.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Доставка.Установить(Доставка);
НаборЗаписей.Отбор.Этап.Установить(Этап);
НаборЗаписей.Прочитать(); // Здесь происходит чтение
Для каждого Запись из НаборЗаписей Цикл
Запись.ДатаВремя = ДатаР;
Запись.Отдел = фио.Подразделение;
Запись.ФИО = ПараметрыСеанса.ТекущийПользователь;
Запись.Документы = Документы;
Запись.Примечание = Примечание;
КонецЦикла;
НаборЗаписей.Записать(); // Здесь происходит запись изменений
Если же мы хотим более эффективно изменить данные, основываясь на результатах запроса, мы можем использовать следующий подход, предложенный в сообщении 6:
// Предположим, РезультатЗапроса - это результат выполнения запроса,
// который мы хотим загрузить в набор записей для модификации
НаборЗаписей = РегистрыСведений.Документооборот.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Доставка.Установить(Доставка); // Отбор для текущего набора записей
НаборЗаписей.Отбор.Этап.Установить(Этап); // Отбор для текущего набора записей
НаборЗаписей.Загрузить(РезультатЗапроса.Выгрузить()); // Загружаем данные из запроса
// Теперь можем пройтись по Записям НабораЗаписей и модифицировать их
Для каждого Запись из НаборЗаписей Цикл
// ... изменения Запись
КонецЦикла;
НаборЗаписей.Записать();
Этот подход позволяет нам использовать всю мощь запросов для выборки и фильтрации данных, а затем эффективно загрузить их в НаборЗаписей для пакетной модификации и записи. Это часто является самым производительным методом для массового обновления данных регистра сведений.
Мы выясним основные причины, по которым объект Запрос превосходит НаборЗаписей.Прочитать() в скорости при выполнении простой выборки данных без их последующей модификации.
Когда мы используем НаборЗаписей.Прочитать(), платформа 1С выполняет следующие действия:
ЗаписьРегистраСведений в оперативной памяти 1С. Это включает выделение памяти, инициализацию свойств, привязку к метаданным и другие внутренние операции.НаборЗаписей.Весь этот процесс создания и заполнения объектов 1С требует значительных вычислительных ресурсов и дополнительного времени. Если нашей целью является только чтение, эти операции по созданию объектов оказываются "бесполезными", поскольку мы не будем их модифицировать.
В случае с Запрос.Выполнить() и ВыборкаИзРезультатаЗапроса, данные извлекаются и предоставляются для доступа по мере необходимости, без обязательного создания полноценных объектов ЗаписьРегистраСведений для каждой строки. Это минимизирует накладные расходы на работу с памятью и процессором.
Запрос предоставляет гораздо более широкие возможности для формирования условий отбора:
>, <, >=, <=, <>).И, ИЛИ, НЕ).ПОДОБНО), датами и числами.СрезПоследних или СрезПервых, которые позволяют эффективно получать актуальные данные на определенный момент времени без необходимости сложной ручной фильтрации.СОЕДИНЕНИЯ), группировать их и использовать агрегатные функции.Отбор в НаборЗаписей значительно ограничен: он позволяет устанавливать условия равенства только для измерений регистра, и эти условия всегда связываются оператором "И". Если нам нужны более сложные условия, то НаборЗаписей просто не подходит.
НаборЗаписей, как мы уже говорили, в первую очередь создан для модификации данных. Его внутренняя структура и методы оптимизированы для эффективного чтения, изменения и записи коллекций записей обратно в базу данных. Если же нам нужно просто "взять и посмотреть" данные, без намерения их менять, то использование инструмента, предназначенного для модификации, приводит к неоправданным накладным расходам.
Запрос же предназначен именно для извлечения данных. Его оптимизация сосредоточена на быстрой и эффективной выборке.
Хотя существуют общие рекомендации, в реальных условиях производительность может зависеть от множества факторов: объема данных, индексов в СУБД, нагрузки на сервер, версии платформы и даже конкретной конфигурации. Поэтому, для критически важных участков кода, всегда рекомендуется проводить собственные замеры. Мы рассмотрим, как это сделать.
Самый простой способ оценить скорость выполнения кода – использовать встроенные средства замера производительности 1С. Мы выполним каждое действие несколько раз и замерим общее время.
// --- Вариант 1: Запрос ---
НачалоЗамера1 = ТекущаяУниверсальнаяДатаВМиллисекундах();
Для И = 1 По 100 Цикл // Повторим несколько раз для получения усредненного результата
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документооборот.ДатаВремя,
| Документооборот.Отдел
|ИЗ
| РегистрСведений.Документооборот КАК Документооборот
|ГДЕ
| Документооборот.Доставка = &Доставка"; // Используем фиктивные данные для замера
Запрос.УстановитьПараметр("Доставка", Справочники.Доставка.НайтиПоНаименованию("ТестоваяДоставка"));
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Просто перебираем, не обрабатываем
Переменная = Выборка.Отдел;
КонецЦикла;
КонецЦикла;
КонецЗамера1 = ТекущаяУниверсальнаяДатаВМиллисекундах();
Сообщить("Запрос: " + (КонецЗамера1 - НачалоЗамера1) + " мс");
// --- Вариант 2: НаборЗаписей.Прочитать() ---
НачалоЗамера2 = ТекущаяУниверсальнаяДатаВМиллисекундах();
Для И = 1 По 100 Цикл // Повторим несколько раз
НаборЗаписей = РегистрыСведений.Документооборот.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Доставка.Установить(Справочники.Доставка.НайтиПоНаименованию("ТестоваяДоставка"));
НаборЗаписей.Прочитать();
Для каждого Запись из НаборЗаписей Цикл
// Просто перебираем, не обрабатываем
Переменная = Запись.Отдел;
КонецЦикла;
КонецЦикла;
КонецЗамера2 = ТекущаяУниверсальнаяДатаВМиллисекундах();
Сообщить("НаборЗаписей.Прочитать(): " + (КонецЗамера2 - НачалоЗамера2) + " мс");
Мы должны убедиться, что используем одни и те же условия отбора и объем данных для обоих тестов. Также важно выполнять замер на тестовом стенде, максимально приближенном к рабочей среде, с соответствующим количеством данных.
Для более глубокого анализа мы можем включить технологический журнал 1С с соответствующими настройками, чтобы он фиксировал SQL-запросы, отправляемые к СУБД. Это позволит нам увидеть, какие именно SQL-запросы формируются платформой в обоих случаях. Мы настроим журнал так, чтобы он выводил события DBMSSQL (для MS SQL Server), DBPOSTGRS (для PostgreSQL) или другие, в зависимости от используемой СУБД, с указанием текста запроса и времени выполнения.
Мы проанализируем следующие аспекты:
EXPLAIN в PostgreSQL), мы можем получить план выполнения SQL-запросов. План покажет, какие индексы используются, какие операции (сканирование таблиц, поиск по индексу, сортировка, объединения) выполняются и сколько ресурсов они потребляют.Анализ плана выполнения поможет нам понять, почему один запрос работает быстрее другого, и выявить "узкие места".
Производительность выборки данных напрямую зависит от правильности настроенных индексов на полях регистра сведений. Мы рассмотрим текущие индексы регистра (измерения, ресурсы, реквизиты) и оценим, соответствуют ли они нашим запросам. Возможно, для ускорения выполнения запроса потребуется добавить новый индекс или изменить порядок существующих измерений, что повлияет на структуру индекса по умолчанию. Мы можем проиндексировать поля, которые часто используются в условиях отбора (в секции ГДЕ запроса) или для сортировки (в секции УПОРЯДОЧИТЬ ПО).
Мы подведем итоги нашего анализа и сформулируем четкие рекомендации по выбору метода работы с регистрами сведений в 1С, исходя из задач, которые перед нами стоят.
Запрос.
Это однозначная рекомендация. Если наша цель – только получить данные из регистра сведений для отображения, анализа или использования в дальнейшем коде без их изменения, то объект Запрос будет наиболее производительным и гибким инструментом. Он обеспечивает прямой и эффективный доступ к СУБД, минимизирует накладные расходы на создание объектов 1С и предоставляет широкие возможности для фильтрации и обработки данных.
Пример: Получить список всех этапов для определенной доставки.
НаборЗаписей.
Объект НаборЗаписей специально разработан для этих целей. Если нам нужно изменить существующие записи, добавить новые или удалить группу записей, то НаборЗаписей является правильным выбором. Он предоставляет удобный объектный интерфейс для работы с коллекциями записей и оптимизирован для пакетной записи изменений в базу данных.
Важно: Избегайте записи отдельных записей регистра сведений в цикле (используя МенеджерЗаписи или создавая НаборЗаписей для каждой записи). Это крайне неэффективно. Всегда старайтесь собрать все изменения в один НаборЗаписей и выполнить одну операцию Записать().
Пример: Изменить статус всех документов, связанных с определенной доставкой.
Запрос + НаборЗаписей.Загрузить().
Если нам нужно найти большой объем данных с помощью сложных условий запроса, а затем массово их изменить, то наиболее эффективным будет следующий алгоритм:
Запрос для выборки необходимых данных.РезультатЗапроса.Выгрузить()).НаборЗаписей, устанавливаем отбор, если нужно.НаборЗаписей с помощью метода НаборЗаписей.Загрузить().НаборЗаписей, вносим изменения.НаборЗаписей.Записать() один раз для всех изменений.Этот подход сочетает в себе гибкость запросов для выборки и эффективность НаборЗаписей для пакетной записи.
РегистрСведенийМенеджерЗаписи: Используйте осторожно.
МенеджерЗаписи предназначен для работы с одной записью регистра, отбор по которой установлен по всем измерениям. Мы применяем его только в тех редких случаях, когда требуется работать с уникальной записью, идентифицируемой по всем измерениям. В остальных сценариях НаборЗаписей или прямой Запрос будут предпочтительнее и производительнее.
Всегда помните, что для критически важных по производительности участков кода мы должны проводить собственные замеры на актуальных данных. Это поможет нам подтвердить общие рекомендации и выявить специфические для нашей системы "узкие места".