Что быстрее: Запрос ... Выполнить() или НаборЗаписей ... Прочитать() для выборки данных из регистра сведений в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Вопрос о производительности между использованием объекта Запрос и метода Прочитать() объекта НаборЗаписей при работе с регистрами сведений является одним из наиболее часто задаваемых в сообществе 1С. Мы с вами детально разберем этот аспект, выясним, какой подход предпочтительнее для простой выборки данных, и проанализируем причины такого выбора.

Если мы говорим исключительно о чтении данных из регистра сведений без их последующей модификации, то, согласно официальным рекомендациям 1С, опубликованным на ИТС, Запрос будет быстрее в большинстве случаев.

Запросы для чтения данных: основной подход

Мы рассмотрим, почему использование объекта Запрос и его метода Выполнить() является предпочтительным для простых выборок из регистра сведений, когда нам не требуется изменять данные. Гибкость и эффективность запросов делают их мощным инструментом для извлечения информации из базы данных.

Преимущества использования объекта Запрос:

  1. Прямой доступ к СУБД: Запрос позволяет напрямую формировать SQL-запросы к системе управления базами данных (СУБД), что минимизирует промежуточные слои обработки платформой 1С. Это означает, что СУБД выполняет работу по выборке данных максимально эффективно, используя свои внутренние оптимизаторы и индексы.
  2. Экономия ресурсов: При использовании Запрос и последующей обработке данных через объект ВыборкаИзРезультатаЗапроса, платформа не загружает все данные в оперативную память сразу в виде объектов 1С. Выборка происходит построчно (или порциями), что значительно снижает потребление оперативной памяти, особенно при работе с большими объемами данных. Мы не создаем избыточных объектов ЗаписьРегистраСведений, которые не будут модифицироваться.
  3. Гибкость и мощь: Запросы 1С предоставляют богатый функционал для фильтрации, сортировки, группировки и объединения данных. Мы можем использовать сложные условия отбора, виртуальные таблицы (например, СрезПоследних, СрезПервых), агрегатные функции, что делает запросы универсальным инструментом для аналитической работы.
  4. Оптимизация на уровне СУБД: СУБД имеет развитые механизмы для оптимизации выполнения SQL-запросов. Хорошо составленный запрос может быть выполнен СУБД очень быстро, особенно если для полей регистра сведений настроены адекватные индексы.

Пример использования запроса для чтения данных:

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Документооборот.ДатаВремя,
    |    Документооборот.Отдел,
    |    Документооборот.ФИО,
    |    Документооборот.Документы,
    |    Документооборот.Примечание
    |ИЗ
    |    РегистрСведений.Документооборот КАК Документооборот
    |ГДЕ
    |    Документооборот.Доставка = &Доставка
    |    И Документооборот.Этап = &Этап";

Запрос.УстановитьПараметр("Доставка", Доставка); // Переменная Доставка должна быть определена
Запрос.УстановитьПараметр("Этап", Этап);       // Переменная Этап должна быть определена

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Мы можем работать с данными выборки
    Сообщить("ДатаВремя: " + Выборка.ДатаВремя + ", Отдел: " + Выборка.Отдел);
    // ... и т.д.
КонецЦикла;

В этом примере мы создаем Запрос, устанавливаем параметры, выполняем его и затем построчно обходим Выборку. Данные извлекаются по мере необходимости, без предварительной полной загрузки в память.

НаборЗаписей: его истинное назначение

Теперь мы обратимся к объекту НаборЗаписей и выясним, когда его использование оправдано, а когда нет, особенно в контексте производительности.

Объект НаборЗаписей (например, РегистрыСведений.ДокументоОборот.СоздатьНаборЗаписей()) в первую очередь предназначен для модификации (изменения, записи и удаления) данных регистра сведений. Его механизм работы отличается от прямого запроса:

  1. Создание объектов 1С: При вызове метода Прочитать() для объекта НаборЗаписей, платформа сначала выполняет внутренний запрос к базе данных, затем загружает все полученные данные и создает коллекцию объектов типа "ЗаписьРегистраСведений" в оперативной памяти. Каждый такой объект является полноценным объектом 1С со всеми его свойствами и методами.
  2. Полная загрузка данных: Весь набор данных, соответствующий установленным отборам, загружается в оперативную память сразу. Это может быть ресурсоемким процессом для больших объемов данных.
  3. Ограниченность отбора: Отбор для НаборЗаписей относительно ограничен. Мы можем устанавливать условия равенства только для измерений регистра, и эти условия связываются логической операцией "И". Более сложные условия, виртуальные таблицы или агрегатные функции недоступны напрямую.

Когда уместно использовать НаборЗаписей:

Мы используем НаборЗаписей, когда нашей основной целью является изменение или запись данных в регистр сведений. Это его прямое и наиболее эффективное назначение:

  1. Изменение существующих записей: Если нам нужно найти записи, изменить их поля и записать обратно.
  2. Добавление новых записей: Для создания одной или нескольких новых записей в регистре.
  3. Массовое обновление данных: Мы можем сначала загрузить данные из запроса в НаборЗаписей с помощью метода Загрузить(), а затем пакетно записать изменения. Это значительно производительнее, чем записывать каждую запись по отдельности в цикле.
  4. Удаление записей: Установив необходимый отбор и вызвав метод Записать() для пустого набора записей, мы можем удалить все записи, соответствующие отбору.

Пример использования НаборЗаписей.Прочитать() (и его модификации) из сообщения на форуме:

Обратите внимание, что даже в примере из сообщения 5, после Прочитать(), записи модифицируются, а затем вызывается НаборЗаписей.Записать(). Это подтверждает, что НаборЗаписей наиболее эффективен для сценариев изменения данных.


НаборЗаписей = РегистрыСведений.ДокументоОборот.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Доставка.Установить(Доставка);
НаборЗаписей.Отбор.Этап.Установить(Этап);
НаборЗаписей.Прочитать(); // Здесь происходит чтение

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

НаборЗаписей.Записать(); // Здесь происходит запись изменений

Если же мы хотим более эффективно изменить данные, основываясь на результатах запроса, мы можем использовать следующий подход, предложенный в сообщении 6:


// Предположим, РезультатЗапроса - это результат выполнения запроса,
// который мы хотим загрузить в набор записей для модификации
НаборЗаписей = РегистрыСведений.Документооборот.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Доставка.Установить(Доставка); // Отбор для текущего набора записей
НаборЗаписей.Отбор.Этап.Установить(Этап);         // Отбор для текущего набора записей

НаборЗаписей.Загрузить(РезультатЗапроса.Выгрузить()); // Загружаем данные из запроса

// Теперь можем пройтись по Записям НабораЗаписей и модифицировать их
Для каждого Запись из НаборЗаписей Цикл
    // ... изменения Запись
КонецЦикла;

НаборЗаписей.Записать();

Этот подход позволяет нам использовать всю мощь запросов для выборки и фильтрации данных, а затем эффективно загрузить их в НаборЗаписей для пакетной модификации и записи. Это часто является самым производительным методом для массового обновления данных регистра сведений.

Почему Запрос ... Выполнить() чаще всего быстрее для чтения

Мы выясним основные причины, по которым объект Запрос превосходит НаборЗаписей.Прочитать() в скорости при выполнении простой выборки данных без их последующей модификации.

  1. Механизм работы:

    Когда мы используем НаборЗаписей.Прочитать(), платформа 1С выполняет следующие действия:

    1. Формирует и выполняет SQL-запрос к СУБД.
    2. Получает данные из СУБД.
    3. Для каждой строки полученных данных создает объект ЗаписьРегистраСведений в оперативной памяти 1С. Это включает выделение памяти, инициализацию свойств, привязку к метаданным и другие внутренние операции.
    4. Заполняет эти объекты данными и добавляет их в коллекцию НаборЗаписей.

    Весь этот процесс создания и заполнения объектов 1С требует значительных вычислительных ресурсов и дополнительного времени. Если нашей целью является только чтение, эти операции по созданию объектов оказываются "бесполезными", поскольку мы не будем их модифицировать.

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

  2. Гибкость отбора:

    Запрос предоставляет гораздо более широкие возможности для формирования условий отбора:

    • Мы можем использовать любые операторы сравнения (>, <, >=, <=, <>).
    • Поддерживаются сложные логические выражения (И, ИЛИ, НЕ).
    • Доступны функции для работы со строками (ПОДОБНО), датами и числами.
    • Мы можем использовать виртуальные таблицы, такие как СрезПоследних или СрезПервых, которые позволяют эффективно получать актуальные данные на определенный момент времени без необходимости сложной ручной фильтрации.
    • Возможность объединять данные из разных таблиц (СОЕДИНЕНИЯ), группировать их и использовать агрегатные функции.

    Отбор в НаборЗаписей значительно ограничен: он позволяет устанавливать условия равенства только для измерений регистра, и эти условия всегда связываются оператором "И". Если нам нужны более сложные условия, то НаборЗаписей просто не подходит.

  3. Предназначение объектов:

    НаборЗаписей, как мы уже говорили, в первую очередь создан для модификации данных. Его внутренняя структура и методы оптимизированы для эффективного чтения, изменения и записи коллекций записей обратно в базу данных. Если же нам нужно просто "взять и посмотреть" данные, без намерения их менять, то использование инструмента, предназначенного для модификации, приводит к неоправданным накладным расходам.

    Запрос же предназначен именно для извлечения данных. Его оптимизация сосредоточена на быстрой и эффективной выборке.

Как провести замер производительности и углубленный анализ

Хотя существуют общие рекомендации, в реальных условиях производительность может зависеть от множества факторов: объема данных, индексов в СУБД, нагрузки на сервер, версии платформы и даже конкретной конфигурации. Поэтому, для критически важных участков кода, всегда рекомендуется проводить собственные замеры. Мы рассмотрим, как это сделать.

  1. Простой замер производительности в 1С:

    Самый простой способ оценить скорость выполнения кода – использовать встроенные средства замера производительности 1С. Мы выполним каждое действие несколько раз и замерим общее время.

    
    // --- Вариант 1: Запрос ---
    НачалоЗамера1 = ТекущаяУниверсальнаяДатаВМиллисекундах();
    Для И = 1 По 100 Цикл // Повторим несколько раз для получения усредненного результата
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |    Документооборот.ДатаВремя,
            |    Документооборот.Отдел
            |ИЗ
            |    РегистрСведений.Документооборот КАК Документооборот
            |ГДЕ
            |    Документооборот.Доставка = &Доставка"; // Используем фиктивные данные для замера
    
        Запрос.УстановитьПараметр("Доставка", Справочники.Доставка.НайтиПоНаименованию("ТестоваяДоставка")); 
    
        РезультатЗапроса = Запрос.Выполнить();
        Выборка = РезультатЗапроса.Выбрать();
        Пока Выборка.Следующий() Цикл
            // Просто перебираем, не обрабатываем
            Переменная = Выборка.Отдел;
        КонецЦикла;
    КонецЦикла;
    КонецЗамера1 = ТекущаяУниверсальнаяДатаВМиллисекундах();
    Сообщить("Запрос: " + (КонецЗамера1 - НачалоЗамера1) + " мс");
    
    // --- Вариант 2: НаборЗаписей.Прочитать() ---
    НачалоЗамера2 = ТекущаяУниверсальнаяДатаВМиллисекундах();
    Для И = 1 По 100 Цикл // Повторим несколько раз
        НаборЗаписей = РегистрыСведений.Документооборот.СоздатьНаборЗаписей();
        НаборЗаписей.Отбор.Доставка.Установить(Справочники.Доставка.НайтиПоНаименованию("ТестоваяДоставка"));
    
        НаборЗаписей.Прочитать();
    
        Для каждого Запись из НаборЗаписей Цикл
            // Просто перебираем, не обрабатываем
            Переменная = Запись.Отдел;
        КонецЦикла;
    КонецЦикла;
    КонецЗамера2 = ТекущаяУниверсальнаяДатаВМиллисекундах();
    Сообщить("НаборЗаписей.Прочитать(): " + (КонецЗамера2 - НачалоЗамера2) + " мс");
    

    Мы должны убедиться, что используем одни и те же условия отбора и объем данных для обоих тестов. Также важно выполнять замер на тестовом стенде, максимально приближенном к рабочей среде, с соответствующим количеством данных.

  2. Технологический журнал и SQL-запросы:

    Для более глубокого анализа мы можем включить технологический журнал 1С с соответствующими настройками, чтобы он фиксировал SQL-запросы, отправляемые к СУБД. Это позволит нам увидеть, какие именно SQL-запросы формируются платформой в обоих случаях. Мы настроим журнал так, чтобы он выводил события DBMSSQL (для MS SQL Server), DBPOSTGRS (для PostgreSQL) или другие, в зависимости от используемой СУБД, с указанием текста запроса и времени выполнения.

    Мы проанализируем следующие аспекты:

    • Текст SQL-запроса: Увидим, есть ли существенные различия в структуре генерируемых SQL-запросов.
    • План выполнения запроса (execution plan): Используя средства СУБД (например, SQL Server Management Studio для MS SQL Server или EXPLAIN в PostgreSQL), мы можем получить план выполнения SQL-запросов. План покажет, какие индексы используются, какие операции (сканирование таблиц, поиск по индексу, сортировка, объединения) выполняются и сколько ресурсов они потребляют.

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

  3. Анализ индексов регистра сведений:

    Производительность выборки данных напрямую зависит от правильности настроенных индексов на полях регистра сведений. Мы рассмотрим текущие индексы регистра (измерения, ресурсы, реквизиты) и оценим, соответствуют ли они нашим запросам. Возможно, для ускорения выполнения запроса потребуется добавить новый индекс или изменить порядок существующих измерений, что повлияет на структуру индекса по умолчанию. Мы можем проиндексировать поля, которые часто используются в условиях отбора (в секции ГДЕ запроса) или для сортировки (в секции УПОРЯДОЧИТЬ ПО).

Итог и рекомендации

Мы подведем итоги нашего анализа и сформулируем четкие рекомендации по выбору метода работы с регистрами сведений в 1С, исходя из задач, которые перед нами стоят.

  1. Для чтения данных без модификации: Используйте Запрос.

    Это однозначная рекомендация. Если наша цель – только получить данные из регистра сведений для отображения, анализа или использования в дальнейшем коде без их изменения, то объект Запрос будет наиболее производительным и гибким инструментом. Он обеспечивает прямой и эффективный доступ к СУБД, минимизирует накладные расходы на создание объектов 1С и предоставляет широкие возможности для фильтрации и обработки данных.

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

  2. Для модификации (изменения, добавления, удаления) данных: Используйте НаборЗаписей.

    Объект НаборЗаписей специально разработан для этих целей. Если нам нужно изменить существующие записи, добавить новые или удалить группу записей, то НаборЗаписей является правильным выбором. Он предоставляет удобный объектный интерфейс для работы с коллекциями записей и оптимизирован для пакетной записи изменений в базу данных.

    Важно: Избегайте записи отдельных записей регистра сведений в цикле (используя МенеджерЗаписи или создавая НаборЗаписей для каждой записи). Это крайне неэффективно. Всегда старайтесь собрать все изменения в один НаборЗаписей и выполнить одну операцию Записать().

    Пример: Изменить статус всех документов, связанных с определенной доставкой.

  3. Комбинированный подход для массового обновления: Запрос + НаборЗаписей.Загрузить().

    Если нам нужно найти большой объем данных с помощью сложных условий запроса, а затем массово их изменить, то наиболее эффективным будет следующий алгоритм:

    1. Выполняем Запрос для выборки необходимых данных.
    2. Выгружаем результат запроса в таблицу значений (РезультатЗапроса.Выгрузить()).
    3. Создаем НаборЗаписей, устанавливаем отбор, если нужно.
    4. Загружаем полученную таблицу значений в НаборЗаписей с помощью метода НаборЗаписей.Загрузить().
    5. Проходим по записям НаборЗаписей, вносим изменения.
    6. Вызываем НаборЗаписей.Записать() один раз для всех изменений.

    Этот подход сочетает в себе гибкость запросов для выборки и эффективность НаборЗаписей для пакетной записи.

  4. Объект РегистрСведенийМенеджерЗаписи: Используйте осторожно.

    МенеджерЗаписи предназначен для работы с одной записью регистра, отбор по которой установлен по всем измерениям. Мы применяем его только в тех редких случаях, когда требуется работать с уникальной записью, идентифицируемой по всем измерениям. В остальных сценариях НаборЗаписей или прямой Запрос будут предпочтительнее и производительнее.

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

← На главную