Как работает выборка из результата запроса в 1С и чем она отличается от массива

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

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

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

Анализ аналогии: Двумерный массив против Курсора

Для начала давайте вернемся к аналогии, предложенной в обсуждении. Автор предполагает следующую ментальную модель:

  1. Метод Запрос.Выполнить() создает двумерный массив с тремя столбцами и n-строками.
  2. Метод РезультатЗапроса.Выбрать() инициализирует счетчик строк (условный индекс i = 0).
  3. Метод Выборка.Следующий() выполняет операцию перехода к следующему элементу (аналог i++) и возвращает Ложь, когда записи закончились.
  4. Обращение вида Выборка[j] возвращает значение конкретной колонки, выступая аналогом конструкции РезультатЗапроса[i][j] из языка Си.

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

Самое главное различие заключается в том, что объект ВыборкаИзРезультатаЗапроса — это не массив. Это классический итератор (в терминологии баз данных — однонаправленный курсор, или Forward-Only Cursor). Если подняться на уровень языка C++, то наиболее близким аналогом выборки будет интерфейс IIterable или паттерн проектирования «Итератор» (Iterator). Выборка не хранит в себе одновременно всю таблицу в виде готового набора объектов строк. Она предоставляет последовательный, строго однонаправленный доступ к результатам запроса.

Что происходит в памяти при выполнении запроса

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

Шаг 1: Выполнение запроса

Когда мы пишем и выполняем следующий код:


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

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

В этот момент платформа 1С отправляет SQL-запрос к СУБД (например, MS SQL Server или PostgreSQL). СУБД обрабатывает запрос, находит нужные записи и возвращает их платформе. Полученный объект РезультатЗапроса — это сырые, максимально сжатые данные, находящиеся в памяти процесса 1С (на стороне сервера). На этом этапе никаких сложных объектных «оберток» для строк еще не создано. Это сделано для экономии оперативной памяти.

Шаг 2: Инициализация выборки

Когда мы пишем:

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

Платформа создает объект-итератор (курсор). В этот момент указатель выборки позиционируется в специальное системное состояние — «перед первой строкой». Никаких данных мы еще не считали, и обратиться к полям выборки на этом этапе нельзя — это вызовет ошибку времени выполнения.

Шаг 3: Обход в цикле

Далее мы запускаем цикл:


Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Фамилия + " " + Выборка.Инициалы + ". Оклад: " + Выборка.Оклад + "р.");
КонецЦикла;

При каждом вызове метода Следующий() происходят следующие действия:

  1. Указатель курсора перемещается на одну строку вперед.
  2. Платформа считывает «сырые» данные текущей строки из результата запроса и динамически формирует свойства объекта выборки (в нашем примере это свойства Фамилия, Инициалы и Оклад).
  3. Метод возвращает Истина, если переход на новую строку прошел успешно.
  4. Если строки закончились, указатель перемещается в состояние «после последней строки», свойства очищаются, и метод возвращает Ложь, завершая цикл.

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

Как работает обращение по индексу в выборке

В ходе обсуждения возник важный вопрос: можно ли использовать квадратные скобки для доступа к данным выборки? Да, платформа 1С поддерживает обращение к полям выборки через оператор [...]. Однако здесь кроется фундаментальное отличие от двумерных массивов в Си.

Если в Си выражение matrix[i][j] означает «перейти к строке с индексом i, а затем взять колонку с индексом j», то в 1С для выборки синтаксис Выборка[j] работает совершенно иначе:

Таким образом, если наш запрос выбирает три поля (0 — Фамилия, 1 — Инициалы, 2 — Оклад), то находясь на определенной строке после вызова Выборка.Следующий(), мы можем обратиться к данным этой строки следующими способами:


// Обращение по имени поля (наиболее предпочтительный и читаемый вариант)
ТекущийОклад = Выборка.Оклад;

// Обращение через квадратные скобки по имени поля
ТекущийОклад = Выборка["Оклад"];

// Обращение через квадратные скобки по индексу колонки (индексация с нуля)
ТекущийОклад = Выборка[2]; 

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

Когда действительно нужен двумерный массив: Таблица Значений

Если для решения вашей бизнес-задачи критически важно иметь произвольный доступ к строкам по их числовому индексу (например, вам нужно сравнить значение в третьей строке со значением в первой строке или быстро перепрыгивать между элементами), выборка вам не подойдет. В этом случае необходимо использовать другой объект платформы — Таблицу Значений.

Чтобы преобразовать результат запроса в таблицу значений, используется метод Выгрузить():

ТаблицаРезультатов = РезультатЗапроса.Выгрузить();

Вот этот объект уже максимально близок к двумерному динамическому массиву с именованными колонками. Давайте проанализируем, как получить доступ к конкретному элементу (например, к окладу во 3-й строке, которая имеет индекс 2):


// Обращение к строке по индексу, а затем к колонке по имени
НужныйОклад = ТаблицаРезультатов[2].Оклад;

// Обращение к строке по индексу, а затем к колонке по ее индексу
НужныйОклад = ТаблицаРезультатов[2][2];

Как мы видим, в случае с Таблицей Значений аналогия с Си-массивом вида matrix[i][j] становится практически стопроцентной. Мы можем свободно обращаться к любой ячейке таблицы, зная индекс строки и индекс (или имя) колонки.

Сравнительный анализ: Выборка против Таблицы значений

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

  1. Потребление оперативной памяти (RAM):
    • Выборка работает в потоковом режиме. Данные находятся в сыром виде и разворачиваются в объекты порционно, по мере обхода. Более того, при работе с гигантскими объемами данных (например, выборка на миллионы строк) платформа умеет автоматически кэшировать данные на жесткий диск во временные файлы, предотвращая переполнение RAM и аварийное завершение процесса.
    • Таблица значений принудительно и единовременно загружает весь объем данных в оперативную память сервера, создавая тяжеловесную объектную структуру (строки, колонки, типизированные ячейки). Выгрузка огромного запроса в таблицу значений может легко привести к ошибке Out of memory.
  2. Направление обхода и доступность данных:
    • Выборка поддерживает только последовательный обход «вперед» (Forward-Only). Произвольный доступ по индексу строки невозможен.
    • Таблица значений поддерживает полный произвольный доступ. Вы можете читать строки в любом порядке, обращаться по индексам, сортировать таблицу программно, добавлять и удалять строки прямо «на лету».
  3. Контекст использования (Клиент-Сервер):
    • РезультатЗапроса и ВыборкаИзРезультатаЗапроса — это чисто серверные объекты. Они содержат указатели на ресурсы базы данных и не могут быть переданы с сервера на тонкий клиент.
    • Таблица значений также является серверным объектом, но при определенных условиях (если она состоит из примитивных типов данных или передается в составе специального реквизита формы) данные из нее могут быть переданы на клиентскую сторону для отображения пользователю.

Важные рекомендации и лучшие практики

Разберем несколько важных практических советов, которые помогут вам писать производительный и безопасный код при работе с запросами в 1С.

1. Оптимальная проверка на пустоту. Часто разработчики совершают ошибку, сначала получая выборку, а затем проверяя, есть ли в ней данные. С точки зрения производительности СУБД и сервера 1С, гораздо эффективнее использовать метод Пустой() самого объекта РезультатЗапроса. Этот метод мгновенно возвращает булево значение, не тратя ресурсы на инициализацию механизмов выборки:


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

Если Не РезультатЗапроса.Пустой() Тогда
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        // Обработка данных
    КонецЦикла;
КонецЕсли;

2. Сортировка данных. Помните, что порядок строк в выборке гарантирован только в том случае, если в самом тексте запроса явно прописана инструкция УПОРЯДОЧИТЬ ПО. Если этого предложения в запросе нет, СУБД возвращает строки в физическом порядке их нахождения в таблицах или индексах, который может непредсказуемо меняться при изменении плана запроса или обновлении статистики базы данных. Выборка сама по себе сортировать данные не умеет.

3. Потоковая обработка больших массивов. Если вам нужно обработать миллион записей (например, пересчитать цены или перезаполнить аналитику в старых документах), никогда не выгружайте результат запроса в таблицу значений. Используйте только последовательный обход выборки. Это спасет ваш сервер от зависаний и нехватки оперативной памяти.

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

← На главную