Работа с данными в 1С:Предприятие часто требует переноса информации из базы в оперативную память для дальнейшей обработки. Один из самых распространённых сценариев — загрузка результатов выборки в таблицу значений. Этот приём используется при формировании отчётов, обработке больших массивов данных, подготовке информации для обмена или просто для удобного анализа в памяти.
На первый взгляд задача кажется тривиальной: выполнил запрос, получил выборку, загрузил в таблицу. Но на практике разработчики сталкиваются с нюансами: от различий в типах данных до проблем производительности при работе с тысячами строк. В этой статье разберём 5 проверенных способов загрузки выборки в таблицу значений, включая оптимизированные подходы для больших объёмов данных, а также типичные ошибки и их решения.
1. Базовый метод: Загрузка через Запрос.Выполнить()
Самый простой и интуитивно понятный способ — использование метода Выполнить() объекта Запрос с последующей загрузкой результата в таблицу значений. Этот метод подходит для большинства стандартных задач, когда объём данных не превышает нескольких сотен строк.
Пример кода:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул,
| Номенклатура.Цена КАК Цена
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";
РезультатЗапроса = Запрос.Выполнить();
ТаблицаЗначений = РезультатЗапроса.Выгрузить();
Особенности метода:
- 🔹 Автоматически создаёт колонки в таблице значений на основе полей выборки
- 🔹 Сохраняет имена колонок и типы данных из запроса
- 🔹 Подходит для выборок с простой структурой (без вложенных таблиц)
⚠️ Внимание: При использовании этого метода в 1С:Предприятие 8.3.20+ может возникать предупреждение о неявном преобразовании типов, если в выборке есть поля с несовместимыми типами (например,ЧислоиСтрокав одной колонке). В таких случаях рекомендуется явно указывать типы при создании таблицы значений.
2. Оптимизированная загрузка: Выборка.Загрузить()
Для выборок с большим количеством строк (от 1000+) эффективнее использовать метод Выборка.Загрузить(). Он позволяет загружать данные порциями, что снижает нагрузку на память и ускоряет обработку.
Пример оптимизированного кода:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 10000 ..."; // Большая выборка
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
ТаблицаЗначений = Новый ТаблицаЗначений;
// Настраиваем колонки заранее для ускорения
ТаблицаЗначений.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
ТаблицаЗначений.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка"));
ТаблицаЗначений.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТаблицаЗначений.Добавить();
НоваяСтрока.Наименование = Выборка.Наименование;
НоваяСтрока.Артикул = Выборка.Артикул;
НоваяСтрока.Цена = Выборка.Цена;
КонецЦикла;
Преимущества подхода:
- 🚀 Контроль над процессом загрузки (можно добавлять обработку каждой строки)
- 🔄 Возможность загружать данные пакетами (например, по 1000 строк)
- 🛠 Гибкость в преобразовании данных "на лету"
3. Загрузка с преобразованием типов данных
Частая проблема при загрузке выборки — несовпадение типов данных между полями выборки и колонками таблицы значений. Например, в базе поле может храниться как Число(10,2), а в таблице значений требуется Строка. В таких случаях нужно явно преобразовывать данные.
Пример с преобразованием:
Выборка = РезультатЗапроса.Выбрать();
ТаблицаЗначений = Новый ТаблицаЗначений;
ТаблицаЗначений.Колонки.Добавить("ДатаДокумента", Новый ОписаниеТипов("Дата"));
ТаблицаЗначений.Колонки.Добавить("СуммаПрописью", Новый ОписаниеТипов("Строка"));
Пока Выборка.Следующий() Цикл
Строка = ТаблицаЗначений.Добавить();
Строка.ДатаДокумента = Выборка.ДатаДокумента; // Дата без изменений
// Преобразуем число в строку прописью
Строка.СуммаПрописью = ЧислоПрописью(Выборка.СуммаДокумента, , , , "руб.");
КонецЦикла;
Типичные преобразования:
| Тип в выборке | Требуемый тип в ТЗ | Метод преобразования |
|---|---|---|
Число |
Строка |
Формат(Число, "ЧДЦ=2; ЧРЗ=Нет") |
Дата |
Строка |
Формат(Дата, "ДФ=dd.MM.yyyy") |
Булево |
Строка |
?Истина : "Да" : "Нет" |
Если в выборке есть поля с возможными значениями NULL, всегда проверяйте их перед преобразованием: Если ЗначениеЗаполнено(Выборка.Поле) Тогда .... Это предотвратит ошибки выполнения.
4. Загрузка вложенных данных (иерархические выборки)
Когда выборка содержит вложенные таблицы (например, данные документа и его табличные части), стандартные методы не подходят. В таких случаях нужно вручную формировать структуру таблицы значений с колонками типа ТаблицаЗначений.
Пример работы с вложенными данными:
Запрос.Текст =
"ВЫБРАТЬ
| Заказ.Номер КАК Номер,
| Заказ.Дата КАК Дата,
| ЗаказСтроки.Номенклатура КАК Номенклатура,
| ЗаказСтроки.Количество КАК Количество
|ИЗ
| Документ.ЗаказКлиента КАК Заказ
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.Товары КАК ЗаказСтроки
| ПО Заказ.Ссылка = ЗаказСтроки.Ссылка";
ТаблицаЗаказов = Новый ТаблицаЗначений;
ТаблицаЗаказов.Колонки.Добавить("Номер");
ТаблицаЗаказов.Колонки.Добавить("Дата");
ТаблицаЗаказов.Колонки.Добавить("Товары", Новый ОписаниеТипов("ТаблицаЗначений"));
ТекущийЗаказ = Неопределено;
Пока Выборка.Следующий() Цикл
Если ТекущийЗаказ <> Выборка.Номер Тогда
ТекущийЗаказ = Выборка.Номер;
СтрокаЗаказа = ТаблицаЗаказов.Добавить();
СтрокаЗаказа.Номер = Выборка.Номер;
СтрокаЗаказа.Дата = Выборка.Дата;
СтрокаЗаказа.Товары = Новый ТаблицаЗначений;
СтрокаЗаказа.Товары.Колонки.Добавить("Номенклатура");
СтрокаЗаказа.Товары.Колонки.Добавить("Количество");
КонецЕсли;
СтрокаТовара = СтрокаЗаказа.Товары.Добавить();
СтрокаТовара.Номенклатура = Выборка.Номенклатура;
СтрокаТовара.Количество = Выборка.Количество;
КонецЦикла;
Особенности работы с иерархией:
- 📌 Требует ручного контроля за группировкой данных
- 📌 Может значительно увеличивать объём используемой памяти
- 📌 Для больших выборок лучше использовать временные таблицы
Как оптимизировать работу с вложенными выборками?
Для выборок более 5000 строк с вложенными данными рекомендуется:
1. Разбивать запрос на несколько простых выборок
2. Использовать временные таблицы для промежуточных результатов
3. Загружать данные в несколько таблиц значений с последующим объединением
5. Пакетная загрузка для больших объёмов данных
При работе с выборками объёмом более 10 000 строк стандартные методы становятся неэффективными. В таких случаях применяют пакетную загрузку с использованием:
- 📦
Массивдля промежуточного хранения - 📦
Структурадля описания колонок - 📦
ПакетныеЗапросыдля уменьшения нагрузки на СУБД
Пример пакетной обработки:
// 1. Создаём описание структуры таблицы
СтруктураКолонок = Новый Структура;
СтруктураКолонок.Вставить("Наименование", Новый ОписаниеТипов("Строка"));
СтруктураКолонок.Вставить("Количество", Новый ОписаниеТипов("Число"));
// 2. Выполняем запрос с пагинацией
РазмерПакета = 5000;
Смещение = 0;
ОбщаяТаблица = Новый ТаблицаЗначений;
Пока Истина Цикл
ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ " + РазмерПакета + " ...
|ГДЕ ... OFFSET " + Смещение;
Результат = Запрос.Выполнить(ТекстЗапроса).Выгрузить();
Если Результат.Количество() = 0 Тогда
Прервать;
КонецЕсли;
// 3. Объединяем результаты
ОбщаяТаблица.Объединить(Результат);
Смещение = Смещение + РазмерПакета;
КонецЦикла;
Критические моменты при пакетной загрузке:
- 🔴 Всегда ограничивайте размер пакета (оптимально 1000-5000 строк)
- 🔴 Используйте транзакции для отката при ошибках
- 🔴 Контролируйте использование памяти через
ГлобальныйКонтекст.ПамятьИспользована()
Определить оптимальный размер пакета|Создать структуру колонок заранее|Настроить обработку ошибок|Предусмотреть очистку памяти после загрузки-->
6. Альтернативные подходы: временные таблицы и внешние источники
Для сложных сценариев, когда данные нужно предварительно обработать или они поступают из внешних источников, используют:
Временные таблицы:
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Наименование,
| СУММА(Движения.Количество) КАК Итого
|ПОМЕСТИТЬ ВТТ_Итоги
|ИЗ ...";
Запрос.Выполнить();
// Далее работаем с временной таблицей
Запрос2.Текст = "ВЫБРАТЬ * ИЗ ВТТ_Итоги";
ТаблицаЗначений = Запрос2.Выполнить().Выгрузить();
Загрузка из внешних источников (XML, JSON, Excel):
// Пример загрузки из JSON
ДанныеJSON = ПолучитьJSONИзВнешнегоИсточника();
ТаблицаЗначений = Новый ТаблицаЗначений;
Для Каждого Элемент Из ДанныеJSON Цикл
Строка = ТаблицаЗначений.Добавить();
Строка.Наименование = Элемент.Наименование;
Строка.Цена = Элемент.Цена;
КонецЦикла;
Когда использовать альтернативные методы:
| Сценарий | Рекомендуемый метод | Преимущества |
|---|---|---|
| Сложные многотабличные выборки | Временные таблицы | Упрощение логики, лучшая читаемость кода |
| Интеграция с внешними системами | Прямая загрузка из JSON/XML | Минимизация промежуточных преобразований |
| Обработка больших файлов (Excel, CSV) | Чтение потоками с постраничной загрузкой | Экономия памяти, контроль процесса |
Временные таблицы особенно эффективны при работе с Microsoft SQL Server, где их обработка оптимизирована на уровне СУБД. В PostgreSQL и IBM DB2 могут быть нюансы с производительностью при больших объёмах данных.
Типичные ошибки и их решения
Даже опытные разработчики сталкиваются с проблемами при загрузке выборок. Вот наиболее распространённые ошибки и способы их избежать:
1. Превышение лимита памяти
⚠️ Внимание: В 1С:Предприятие 8.3 (особенно в веб-клиенте) лимит памяти на одну операцию составляет ~250 МБ. При загрузке выборок более 50 000 строк используйте пакетную обработку или серверные процедуры.
2. Несовпадение типов данных
- 🔧 Проблема: Ошибка "Тип не соответствует типу колонки"
- 🔧 Решение: Явно указывайте типы при создании колонок или используйте
ЗначениеВСтрокуВнутр()для универсального преобразования
3. Потеря данных при объединении таблиц
- 🔧 Проблема: При использовании
Объединить()дублирующиеся строки замещаются - 🔧 Решение: Используйте
Добавить()в цикле илиОбъединитьПоКлючам()с явным указанием ключевых полей
4. Зависание при выполнении запроса
- 🔧 Проблема: Сложный запрос выполняется более 30 секунд
- 🔧 Решение: Разбивайте запрос на части, используйте индексы, анализируйте план выполнения через
Запрос.Выполнить().ПланВременныхТаблиц()
FAQ: Ответы на частые вопросы
Можно ли загрузить выборку в таблицу значений без указания структуры колонок?
Да, метод РезультатЗапроса.Выгрузить() автоматически создаёт колонки на основе структуры выборки. Однако для больших таблиц (1000+ строк) рекомендуется явно описывать колонки заранее — это ускоряет процесс на 15-30%.
Как загрузить данные из выборки с группировкой (ITOG BY) в таблицу значений?
При использовании ITOG BY в запросе результат содержит иерархическую структуру. Для загрузки в таблицу значений нужно:
- Создать колонку типа
ТаблицаЗначенийдля вложенных данных - В цикле обработки выборки проверять уровень группировки через
Выборка.Уровень() - Для строк с уровнем > 0 добавлять данные во вложенную таблицу
Пример кода доступен в разделе про вложенные данные.
Почему при загрузке больших выборок 1С выдаёт ошибку "Недостаточно памяти"?
Эта ошибка возникает при попытке загрузить в память более 200-300 тыс. строк за один раз. Решения:
- Используйте пакетную загрузку (разбивайте на порции по 5-10 тыс. строк)
- Переносите логику обработки на сервер (используйте серверные процедуры)
- Оптимизируйте запрос (добавляйте индексы, убирайте ненужные поля)
- Для отчётов используйте
ПостроительОтчётавместо таблиц значений
В 1С:Предприятие 8.3.22+ появился механизм потоковой обработки данных (ПотокДанных), который может помочь в таких случаях.
Как ускорить загрузку выборки в таблицу значений в управляемых формах?
В управляемых формах загрузка больших таблиц значений может тормозить интерфейс. Оптимизация:
- Выполняйте загрузку в фоновом задании (
ФоновоеЗадание) - Используйте
ОтображатьПанельПрогресса()для индикации процесса - После загрузки применяйте
УстановитьДанныеФормыВКэш() - Для динамических списков используйте
ОграничениеКоличестваСтрок
Можно ли загрузить в таблицу значений данные из выборки с полями типа "ХранилищеЗначения"?
Поля типа ХранилищеЗначения нельзя напрямую загрузить в таблицу значений стандартными методами. Варианты решений:
- Преобразовывайте хранилище в строку через
ЗначениеВСтрокуВнутр() - Создавайте в таблице значений колонку типа
ДвоичныеДанныеи сохраняйте сериализованное хранилище - Для сложных объектов (например, таблиц значений внутри хранилища) используйте
ЗначениеИзСтрокиВнутр()после загрузки
Пример:
Строка.ДанныеХранилища = ЗначениеВСтрокуВнутр(Выборка.Хранилище);
...
// При необходимости:
ВосстановленноеХранилище = ЗначениеИзСтрокиВнутр(Строка.ДанныеХранилища);