Работа с запросами в 1С:Предприятие — одна из самых востребованных задач среди разработчиков и аналитиков. Часто требуется не просто выполнить запрос к базе данных, а именно получить конкретное значение из его результата: сумму, количество записей, имя клиента или дату документа. Однако начинающие программисты сталкиваются с типичными проблемами: как правильно обработать результат, избежать ошибок приведения типов или оптимизировать код для больших выборок.
В этой статье разберём все актуальные способы извлечения данных из запросов 1С, от элементарного метода Выполнить() до работы с временными таблицами и пакетной обработкой. Особое внимание уделим нюансам, которые не описаны в официальной документации, но критичны для реальных задач — например, как получить значение из первой строки выборки без цикла или почему иногда Выгрузить() работает медленнее, чем прямая выборка.
Материал будет полезен как новичкам, так и опытным специалистам: первые найдут здесь пошаговые инструкции с примерами кода, а вторые — оптимизированные подходы для работы с большими объёмами данных. Все примеры тестировались на платформе 1С:Предприятие 8.3.20 (актуальной на момент публикации), но применимы и к более ранним версиям с учётом синтаксических особенностей.
1. Базовый метод: Выполнить() для одиночных значений
Самый простой способ получить значение из запроса — использовать метод Выполнить() с конструкцией ВЫБРАТЬ ПЕРВЫЕ. Этот подход идеален, когда вам нужно одно конкретное значение: например, сумма по документу, количество строк в справочнике или максимальная дата.
Пример кода для получения количества клиентов в справочнике Контрагенты:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| COUNT(*) КАК Количество
|ИЗ
| Справочник.Контрагенты";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Если Выборка.Следующий() Тогда
Сообщить("Всего контрагентов: " + Выборка.Количество);
КонецЕсли;
✅ Плюсы метода:
- 🔹 Минимальный код — достаточно 5-6 строк
- 🔹 Быстрое выполнение для простых запросов
- 🔹 Подходит для агрегатных функций (
COUNT,SUM,MAX)
⚠️ Ограничения:
- 🚫 Не подходит для выборки нескольких значений (нужен цикл)
- 🚫 При ошибке в запросе вернёт исключение, которое нужно обрабатывать
Если запрос возвращает пустой результат, метод Выборка.Следующий() вернёт Ложь. Всегда проверяйте это условие, чтобы избежать ошибок при обращении к несуществующим данным.
2. Выгрузка результата в коллекцию: Выгрузить()
Когда нужно получить несколько значений или всю выборку целиком, удобнее использовать метод Выгрузить(). Он преобразует результат запроса в одну из коллекций 1С: Массив, СписокЗначений, ТаблицаЗначений или ДеревоЗначений. Это упрощает дальнейшую обработку данных, особенно если требуется фильтрация или сортировка.
Сравнение форматов выгрузки:
| Формат | Когда использовать | Пример применения |
|---|---|---|
ТаблицаЗначений |
Для структурированных данных с колонками | Экспорт в 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) КАК Итого
Почему запрос выполняется долго, хотя данных мало?
Причины могут быть разные:
- Отсутствие индексов на полях, используемых в условиях
ГДЕилиСОЕДИНЕНИЕ. - Сложные подзапросы — замените их на временные таблицы.
- Блокировки другими сеансами (проверьте в
Администрирование → Активные пользователи). - Неэффективные функции в запросе (например,
ПОДСТРОКАилиВЫРАЗИТЬ).
Используйте ПояснитьЗапрос(), чтобы найти узкие места.
Можно ли получить результат запроса в формате JSON?
Прямой поддержки JSON в результатах запросов нет, но вы можете:
- Выгрузить данные в
ТаблицуЗначенийи преобразовать её в JSON с помощью функцииЗаписатьJSON(). - Использовать внешние компоненты (например, 1Script.JSON).
Пример преобразования:
Таблица = Результат.Выгрузить();
СтрокаJSON = Новый ЗаписьJSON;
СтрокаJSON.УстановитьСтроку();
ЗаписатьJSON(СтрокаJSON, Таблица);
РезультатJSON = СтрокаJSON.Закрыть();
Как передать результат запроса в отчёт?
Самый надёжный способ — выгрузить данные в ТаблицуЗначений и передать её как параметр:
Процедура СформироватьОтчёт()
Запрос = Новый Запрос("ВЫБРАТЬ ...");
Результат = Запрос.Выполнить();
Данные = Результат.Выгрузить();
Отчёт = ПолучитьОбъект("Отчёт.МойОтчёт");
Отчёт.Параметры.Данные = Данные;
Отчёт.Сформировать();
КонецПроцедуры
В самом отчёте настройте схему компоновки данных для работы с переданной таблицей.
Что делать, если запрос возвращает слишком много данных?
Ограничьте выборку одним из способов:
- Добавьте
ПЕРВЫЕ Nв текст запроса. - Используйте пакетную обработку (см. раздел 7).
- Фильтруйте данные на уровне запроса (условие
ГДЕ). - Для отчётов настройте отбор в схеме компоновки.
Пример ограничения:
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1000 ...";