Работа с запросами в 1С:Предприятие — одна из самых востребованных задач среди разработчиков и аналитиков. Часто требуется не просто выполнить запрос к базе данных, а именно получить конкретное значение из его результата: сумму, количество записей, имя клиента или дату документа. Однако начинающие программисты сталкиваются с типичными проблемами: как правильно обработать результат, избежать ошибок приведения типов или оптимизировать код для больших выборок.

В этой статье разберём все актуальные способы извлечения данных из запросов 1С, от элементарного метода Выполнить() до работы с временными таблицами и пакетной обработкой. Особое внимание уделим нюансам, которые не описаны в официальной документации, но критичны для реальных задач — например, как получить значение из первой строки выборки без цикла или почему иногда Выгрузить() работает медленнее, чем прямая выборка.

Материал будет полезен как новичкам, так и опытным специалистам: первые найдут здесь пошаговые инструкции с примерами кода, а вторые — оптимизированные подходы для работы с большими объёмами данных. Все примеры тестировались на платформе 1С:Предприятие 8.3.20 (актуальной на момент публикации), но применимы и к более ранним версиям с учётом синтаксических особенностей.

1. Базовый метод: Выполнить() для одиночных значений

Самый простой способ получить значение из запроса — использовать метод Выполнить() с конструкцией ВЫБРАТЬ ПЕРВЫЕ. Этот подход идеален, когда вам нужно одно конкретное значение: например, сумма по документу, количество строк в справочнике или максимальная дата.

Пример кода для получения количества клиентов в справочнике Контрагенты:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ ПЕРВЫЕ 1

| COUNT(*) КАК Количество

|ИЗ

| Справочник.Контрагенты";

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

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

Если Выборка.Следующий() Тогда

Сообщить("Всего контрагентов: " + Выборка.Количество);

КонецЕсли;

Плюсы метода:

  • 🔹 Минимальный код — достаточно 5-6 строк
  • 🔹 Быстрое выполнение для простых запросов
  • 🔹 Подходит для агрегатных функций (COUNT, SUM, MAX)

⚠️ Ограничения:

  • 🚫 Не подходит для выборки нескольких значений (нужен цикл)
  • 🚫 При ошибке в запросе вернёт исключение, которое нужно обрабатывать
💡

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

2. Выгрузка результата в коллекцию: Выгрузить()

Когда нужно получить несколько значений или всю выборку целиком, удобнее использовать метод Выгрузить(). Он преобразует результат запроса в одну из коллекций : Массив, СписокЗначений, ТаблицаЗначений или ДеревоЗначений. Это упрощает дальнейшую обработку данных, особенно если требуется фильтрация или сортировка.

Сравнение форматов выгрузки:

Формат Когда использовать Пример применения
ТаблицаЗначений Для структурированных данных с колонками Экспорт в Excel, обработка больших выборок
СписокЗначений Для одномерных данных (например, список имен) Заполнение выпадающего списка в форме
Массив Для минимального потребления памяти Обработка в циклах с индексами

Пример выгрузки в ТаблицуЗначений:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Наименование КАК Товар,

| СУММА(Документ.РасходнаяНакладная.Количество) КАК Итого

|ИЗ

| Документ.РасходнаяНакладная КАК Документ

| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура

| ПО Документ.Номенклатура = Номенклатура.Ссылка

|ГДЕ

| Документ.Дата МЕЖДУ &НачалоПериода И &КонецПериода

|СГРУППИРОВАТЬ ПО

| Номенклатура.Наименование";

Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));

Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата()));

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

ТаблицаДанных = Результат.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);

📊 Какой формат выгрузки вы используете чаще всего?
ТаблицаЗначений
СписокЗначений
Массив
ДеревоЗначений

3. Прямая работа с выборкой: когда Выгрузить() неэффективна

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

Пример обработки большой выборки без выгрузки в память:

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ ..."; // Ваш запрос с большим количеством строк

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

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

Пока Выборка.Следующий() Цикл

// Обрабатываем текущую строку

Если Выборка.Количество > 1000 Тогда

Сообщить("Крупная партия: " + Выборка.Наименование);

КонецЕсли;

КонецЦикла;

⚠️ Критические нюансы:

⚠️ Внимание: При работе с выборкой через Выбрать() нельзя использовать модификаторы ИНДЕКСИРОВАТЬ ПО или УПОРЯДОЧИТЬ ПО в тексте запроса — это приведёт к ошибке. Для сортировки используйте отдельный запрос или обработайте данные после выборки.

Преимущества прямой выборки:

  • 🔹 Минимальное потребление памяти
  • 🔹 Возможность прервать обработку досрочно (например, при нахождении искомого значения)
  • 🔹 Поддержка пакетной обработки (см. следующий раздел)

Использовать Выбрать() вместо Выгрузить()|Ограничивать выборку условием ПЕРВЫЕ N|Обрабатывать данные порциями по 100-500 строк|Закрывать выборку после использования (Выборка.Закрыть())-->

4. Работа с временными таблицами: сложные многоэтапные запросы

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

Пример использования временной таблицы для анализа продаж:

Запрос = Новый Запрос;

Запрос.Текст =

"// Шаг 1: Создаём временную таблицу с отфильтрованными данными

|ВЫБРАТЬ

| РасходнаяНакладная.Контрагент КАК Клиент,

| РасходнаяНакладная.Дата,

| РасходнаяНакладнаяСтрока.Номенклатура,

| РасходнаяНакладнаяСтрока.Количество

|ПОМЕСТИТЬ ВТ_Продажи

|ИЗ

| Документ.РасходнаяНакладная КАК РасходнаяНакладная

| ЛЕВОЕ СОЕДИНЕНИЕ Документ.РасходнаяНакладная.Товары КАК РасходнаяНакладнаяСтрока

| ПО РасходнаяНакладная.Ссылка = РасходнаяНакладнаяСтрока.Ссылка

|ГДЕ

| РасходнаяНакладная.Дата МЕЖДУ &Начало И &Конец

|

|//////////////////////////////////////////////////

|// Шаг 2: Агрегируем данные из временной таблицы

|ВЫБРАТЬ

| ВТ_Продажи.Клиент КАК Покупатель,

| ВТ_Продажи.Номенклатура,

| СУММА(ВТ_Продажи.Количество) КАК ОбщийОбъем

|ИЗ

| ВТ_Продажи КАК ВТ_Продажи

|СГРУППИРОВАТЬ ПО

| ВТ_Продажи.Клиент,

| ВТ_Продажи.Номенклатура

|УПОРЯДОЧИТЬ ПО

| ОбщийОбъем УБЫВ";

Запрос.УстановитьПараметр("Начало", НачалоКвартала(ТекущаяДата()));

Запрос.УстановитьПараметр("Конец", КонецКвартала(ТекущаяДата()));

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

🔍 Когда использовать временные таблицы:

  • 📊 Для многоэтапной обработки данных (фильтрация → агрегация → сортировка)
  • 🔄 Когда нужно соединить результаты нескольких подзапросов
  • 📈 Для оптимизации производительности сложных отчётов
Как очистить временную таблицу после использования?

Временные таблицы в 1С автоматически очищаются после выполнения запроса, в котором они были созданы. Однако если вы используете их в нескольких запросах подряд (например, в транзакции), данные будут доступны до завершения сеанса или явной очистки через ОЧИСТИТЬ ВТ_ИмяТаблицы.

5. Получение значения из первой строки без цикла

Частая задача — получить значение из первой строки результата (например, самый ранний документ или максимальную сумму). Многие разработчики для этого пишут цикл с условием Если Выборка.Следующий() Тогда, но это избыточно. Достаточно использовать комбинацию ПЕРВЫЕ 1 и прямого обращения к выборке.

Оптимальный способ:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ ПЕРВЫЕ 1

| Документ.Счёт.Номер КАК НомерСчёта

|ИЗ

| Документ.Счёт

|УПОРЯДОЧИТЬ ПО

| Дата УБЫВ"; // Берём самый свежий счёт

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

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

Если Выборка.Следующий() Тогда

ПоследнийНомерСчёта = Выборка.НомерСчёта;

Иначе

ПоследнийНомерСчёта = Неопределёно;

КонецЕсли;

⚠️ Важно:

⚠️ Внимание: Если в запросе не указано УПОРЯДОЧИТЬ ПО, порядок строк не гарантируется! Всегда явно сортируйте данные, если вам важна последовательность.
💡

Для получения единственного значения из запроса достаточно конструкции ВЫБРАТЬ ПЕРВЫЕ 1 + одно обращение к Выборка.Следующий(). Цикл не нужен!

6. Обработка результатов с группировкой: ОбходРезультатаЗапроса

При работе с группировками (например, СГРУППИРОВАТЬ ПО) результат запроса имеет иерархическую структуру. Чтобы корректно его обработать, нужно использовать параметр ОбходРезультатаЗапроса.ПоГруппировкам. Это актуально для отчётов, где требуется показывать как детализированные данные, так и итоги по группам.

Пример обработки группированного результата:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Контрагент.Наименование КАК Клиент,

| Номенклатура.Наименование КАК Товар,

| СУММА(Количество) КАК Объём

|ИЗ

| Документ.РеализацияТоваровУслуг КАК Документ

| ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК Строка

| ПО Документ.Ссылка = Строка.Ссылка

|СГРУППИРОВАТЬ ПО

| Контрагент.Наименование,

| Номенклатура.Наименование";

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

Выборка = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока Выборка.Следующий() Цикл

// Уровень группировки по клиенту

Сообщить("Клиент: " + Выборка.Клиент);

ВыборкаДетализации = Выборка.Выбрать();

Пока ВыборкаДетализации.Следующий() Цикл

// Уровень детализации по товарам

Сообщить(" - " + ВыборкаДетализации.Товар +

": " + ВыборкаДетализации.Объём);

КонецЦикла;

КонецЦикла;

📌 Типичные ошибки при работе с группировками:

  • 🚨 Забывают указать ОбходРезультатаЗапроса.ПоГруппировкам — в результате получают только верхний уровень
  • 🚨 Пытаются обработать детализацию без вложенного цикла
  • 🚨 Не закрывают выборку детализации (ВыборкаДетализации.Закрыть()), что приводит к утечкам памяти

7. Пакетная обработка результатов: оптимизация для больших данных

Если вам нужно обработать десятки тысяч строк (например, при миграции данных или массовом обновлении), прямая выборка может занять слишком много времени. В таких случаях используют пакетную обработку: данные читаются и обрабатываются порциями по 100-1000 строк.

Пример пакетной обработки:

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ ..."; // Запрос с большим количеством строк

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

Запрос.Текст = Запрос.Текст + " УПОРЯДОЧИТЬ ПО &Порядок";

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

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

РазмерПакета = 500; // Обрабатываем по 500 строк за раз

Пока Истина Цикл

ДанныеПакета = Новый Массив;

Счётчик = 0;

// Читаем пакет строк

Пока Выборка.Следующий() И Счётчик < РазмерПакета Цикл

ДанныеПакета.Добавить(Выборка.Ссылка);

Счётчик = Счётчик + 1;

КонецЦикла;

// Обрабатываем пакет

Если ДанныеПакета.Количество() > 0 Тогда

ОбработатьПакетДанных(ДанныеПакета); // Ваша функция обработки

Иначе

Прервать; // Данные закончились

КонецЕсли;

// Опционально: фиксируем прогресс

Сообщить("Обработано: " + (Результат.Выбранно() - Выборка.Осталось()));

КонецЦикла;

🔧 Советы по настройке пакетной обработки:

  • 🔧 Оптимальный размер пакета — 200-1000 строк. Меньше — слишком много транзакций, больше — риск блокировок
  • 🔧 Всегда сортируйте данные по ключевому полю (например, Ссылка), чтобы избежать пропусков при пагинации
  • 🔧 Используйте Транзакция для каждого пакета, если требуется атомарность операций
Как выбрать оптимальный размер пакета?

Начните с 500 строк и замерьте время обработки. Если транзакции занимают >1 секунды, уменьшите пакет до 200. Если обработка идёт слишком быстро (например, по сети), увеличьте до 1000. Главный критерий — отсутствие таймаутов и блокировок в базе.

8. Типичные ошибки и как их избежать

Даже опытные разработчики иногда сталкиваются с неочевидными проблемами при работе с результатами запросов. Вот наиболее распространённые ошибки и способы их решения:

Ошибка Причина Решение
Поле объекта не обнаружено (Выборка.Поле) Опечатка в имени поля или оно не выбрано в запросе Проверьте текст запроса и aliases (КАК)
Недопустимое приведение типов Попытка присвоить NULL в переменную несовместимого типа Используйте ЗначениеЗаполнено() перед приведением
Зависание при выполнении запроса Отсутствует индекс по полю в условии ГДЕ Проверьте план выполнения запроса в консоли
Выборка уже закрыта Повторное использование выборки после Закрыть() Создавайте новую выборку для каждого обхода

⚠️ Скрытые ловушки:

⚠️ Внимание: При использовании ВЫГРУЗИТЬ() с параметром ОбходРезультатаЗапроса.ПоГруппировкам структура выходной таблицы будет иерархической. Если вы ожидаете плоский список, используйте ОбходРезультатаЗапроса.БезГруппировок.

🛠 Инструменты для отладки:

  • 🔍 ПояснитьЗапрос() — показывает план выполнения
  • 🔍 Консоль запросов в конфигураторе (Сервис → Консоль запросов)
  • 🔍 Журнал регистрации для анализа долгих запросов

FAQ: Ответы на частые вопросы

Как получить значение из запроса, если оно может быть NULL?

Используйте функцию ЗначениеЗаполнено() для проверки:

Если ЗначениеЗаполнено(Выборка.Поле) Тогда

// Обработка непустого значения

Иначе

// Альтернативная логика (например, подстановка значения по умолчанию)

КонецЕсли;

Для числовых полей можно использовать ЕСТЬNULL прямо в тексте запроса:

ВЫБРАТЬ

ЕСТЬNULL(СУММА(Количество), 0) КАК Итого

Почему запрос выполняется долго, хотя данных мало?

Причины могут быть разные:

  1. Отсутствие индексов на полях, используемых в условиях ГДЕ или СОЕДИНЕНИЕ.
  2. Сложные подзапросы — замените их на временные таблицы.
  3. Блокировки другими сеансами (проверьте в Администрирование → Активные пользователи).
  4. Неэффективные функции в запросе (например, ПОДСТРОКА или ВЫРАЗИТЬ).

Используйте ПояснитьЗапрос(), чтобы найти узкие места.

Можно ли получить результат запроса в формате JSON?

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

  1. Выгрузить данные в ТаблицуЗначений и преобразовать её в JSON с помощью функции ЗаписатьJSON().
  2. Использовать внешние компоненты (например, 1Script.JSON).

Пример преобразования:

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

СтрокаJSON = Новый ЗаписьJSON;

СтрокаJSON.УстановитьСтроку();

ЗаписатьJSON(СтрокаJSON, Таблица);

РезультатJSON = СтрокаJSON.Закрыть();

Как передать результат запроса в отчёт?

Самый надёжный способ — выгрузить данные в ТаблицуЗначений и передать её как параметр:

Процедура СформироватьОтчёт()

Запрос = Новый Запрос("ВЫБРАТЬ ...");

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

Данные = Результат.Выгрузить();

Отчёт = ПолучитьОбъект("Отчёт.МойОтчёт");

Отчёт.Параметры.Данные = Данные;

Отчёт.Сформировать();

КонецПроцедуры

В самом отчёте настройте схему компоновки данных для работы с переданной таблицей.

Что делать, если запрос возвращает слишком много данных?

Ограничьте выборку одним из способов:

  • Добавьте ПЕРВЫЕ N в текст запроса.
  • Используйте пакетную обработку (см. раздел 7).
  • Фильтруйте данные на уровне запроса (условие ГДЕ).
  • Для отчётов настройте отбор в схеме компоновки.

Пример ограничения:

Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1000 ...";