Работа с таблицами значений в 1С:Предприятие — одна из самых востребованных задач при разработке отчетов, обработок и интеграционных решений. Эти динамические структуры данных позволяют гибко манипулировать информацией прямо в памяти, не прибегая к постоянному обращению к базе. Однако когда дело доходит до их обработки через запросы 1С, многие разработчики сталкиваются с нюансами: от синтаксических особенностей до проблем производительности.
В этой статье мы разберем не только базовые приемы работы с таблицами значений в запросах (включая ВЫБРАТЬ, ГДЕ, СГРУППИРОВАТЬ ПО), но и продвинутые техники: временные таблицы, объединение данных из нескольких источников, оптимизацию тяжелых операций. Особое внимание уделим типичным ошибкам, которые приводят к падению производительности или некорректным результатам — например, когда таблица значений неверно интерпретируется как таблица базы данных.
Материал будет полезен как начинающим специалистам, которые только осваивают встроенный язык 1С, так и опытным программистам, ищущим способы ускорить обработку больших массивов данных. Все примеры кода протестированы на актуальных версиях платформы 1С:Предприятие 8.3 (включая 8.3.23).
1. Основы: что такое таблица значений и как она связана с запросами
Таблица значений в 1С — это объект в памяти, который хранит данные в виде строк и колонок, аналогично таблице базы данных, но без привязки к физическому хранилищу. Она создается динамически в ходе выполнения кода и может содержать любые типы данных: числа, строки, даты, ссылки на справочники и даже другие таблицы значений.
Главное отличие от таблиц базы данных — отсутствие индексов. Это означает, что операции поиска и сортировки в таблицах значений выполняются медленнее, чем в SQL-таблицах, но зато они не требуют транзакций и блокировок. В запросах 1С таблицы значений можно использовать как:
- 📌 Источник данных — когда вы передаете таблицу значений в запрос через параметр
ИЗ &Таблица. - 📌 Приемник результата — когда результат запроса выгружается в новую таблицу значений.
- 📌 Промежуточный буфер — для временного хранения данных между этапами обработки.
Важно понимать, что при работе с таблицами значений в запросах 1С преобразует их во временные таблицы SQL (если используется СУБД). Это накладывает ограничения: например, нельзя напрямую изменять данные в таблице значений через ОБНОВИТЬ или УДАЛИТЬ — для этого потребуется обход строк в цикле.
2. Как передать таблицу значений в запрос: синтаксис и примеры
Чтобы использовать таблицу значений в запросе, ее нужно передать как параметр с префиксом &. Рассмотрим базовый шаблон:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТаблицаСотрудников.ФИО КАК ФИО,
| ТаблицаСотрудников.Должность КАК Должность
|ИЗ
| &ТаблицаСотрудников КАК ТаблицаСотрудников
|ГДЕ
| ТаблицаСотрудников.Стаж > 5";
Запрос.УстановитьПараметр("ТаблицаСотрудников", ТаблицаЗначенийСотрудников);
Результат = Запрос.Выполнить();
Ключевые моменты:
- 🔹 Имя параметра в запросе (
&ТаблицаСотрудников) должно совпадать с именем при установке параметра. - 🔹 В теле запроса таблица значений указывается как обычная таблица базы данных, но с псевдонимом (
КАК ТаблицаСотрудников). - 🔹 Колонки таблицы значений в запросе обращаются по их реальным именам (регистр важен!).
Если таблица значений содержит колонки с составными типами (например, ссылки на справочники), их можно использовать в запросе для фильтрации или соединения с другими таблицами. Например:
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Наименование КАК Товар,
| ТаблицаЦен.Цена КАК Цена
|ИЗ
| Справочник.Товары КАК Товары
| ЛЕВОЕ СОЕДИНЕНИЕ &ТаблицаЦен КАК ТаблицаЦен
| ПО Товары.Ссылка = ТаблицаЦен.Товар";
Если в таблице значений есть колонка с типом "Ссылка", в запросе ее можно сравнивать напрямую со ссылками из базы данных. Но для ускорения работы лучше заранее добавить в таблицу значений колонку с УникальныйИдентификатор() ссылки и фильтровать по ней.
3. Типичные ошибки при работе с таблицами значений в запросах
Даже опытные разработчики иногда допускают ошибки, которые приводят к некорректным результатам или падению производительности. Вот самые распространенные:
| Ошибка | Причина | Как избежать |
|---|---|---|
| Запрос возвращает пустой результат | Не совпадают имена колонок в таблице значений и запросе (регистр имеет значение!) | Проверьте имена колонок через ТаблицаЗначений.Колонки |
| Ошибка "Неопределенное имя колонки" | В таблице значений нет колонки, указанной в запросе | Добавьте недостающую колонку или исправьте запрос |
| Медленное выполнение запроса | Таблица значений слишком большая (тысячи строк) и не оптимизирована | Используйте ИНДЕКСИРОВАТЬ ПО для часто фильтруемых колонок |
| Неправильная сортировка | Сортировка применяется к таблице значений после выполнения запроса, а не в самом запросе | Добавьте УПОРЯДОЧИТЬ ПО в текст запроса |
Особенно коварна ошибка с неявным преобразованием типов. Например, если в таблице значений колонка имеет тип "Строка", а в запросе вы сравниваете ее с числом, 1С попытается автоматически преобразовать данные, что может привести к неожиданным результатам. Всегда проверяйте типы колонок:
Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
Сообщить(Колонка.Имя + ": " + ТипЗнч(Колонка.Значение));
КонецЦикла;
Почему запрос с таблицей значений работает медленнее, чем с базой данных?
При использовании таблицы значений в запросе 1С создает временную таблицу в SQL, но без индексов. Кроме того, данные передаются между 1С и СУБД, что добавляет накладные расходы. Для больших объемов данных (10 000+ строк) лучше использовать временные таблицы базы данных или оптимизировать структуру таблицы значений (например, оставлять только необходимые колонки).
4. Продвинутые техники: временные таблицы, объединения и подзапросы
Когда стандартных возможностей недостаточно, на помощь приходят продвинутые приемы. Рассмотрим три самых полезных:
4.1. Использование временных таблиц
Если таблица значений слишком велика, ее можно предварительно загрузить во временную таблицу базы данных. Это ускорит последующие запросы:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товар КАК Товар,
| СУММА(Количество) КАК ИтогоКоличество
|ПОМЕСТИТЬ ВТ_ИтогиПоТоварам
|ИЗ
| &ТаблицаДвижений КАК ТаблицаДвижений
|СГРУППИРОВАТЬ ПО
| Товар";
Запрос.УстановитьПараметр("ТаблицаДвижений", ТаблицаДвижений);
Запрос.Выполнить();
// Теперь можно работать с временной таблицей ВТ_ИтогиПоТоварам
Запрос2 = Новый Запрос;
Запрос2.Текст = "ВЫБРАТЬ * ИЗ ВТ_ИтогиПоТоварам";
4.2. Объединение данных из нескольких таблиц значений
Для объединения данных из нескольких источников используйте конструкцию ОБЪЕДИНИТЬ:
Запрос.Текст =
"ВЫБРАТЬ
| ФИО,
| 'Основной штат' КАК Источник
|ИЗ
| &ОсновнойШтат КАК ОсновнойШтат
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ
| ФИО,
| 'Временный персонал' КАК Источник
|ИЗ
| &ВременныйПерсонал КАК ВременныйПерсонал";
4.3. Подзапросы с таблицами значений
Таблицы значений можно использовать и в подзапросах. Например, для фильтрации данных по динамически сформированному списку:
Запрос.Текст =
"ВЫБРАТЬ
| Справочник.Контрагенты.Наименование
|ИЗ
| Справочник.Контрагенты КАК Справочник.Контрагенты
|ГДЕ
| Справочник.Контрагенты.Ссылка В (
| ВЫБРАТЬ
| Контрагент
| ИЗ
| &СписокКонтрагентов КАК СписокКонтрагентов
| )";
Удалить ненужные колонки|Проверить типы данных колонок|Добавить индексы для часто используемых колонок|Оптимизировать количество строк (оставить только релевантные)|Проверить на наличие пустых значений-->
5. Оптимизация производительности: как ускорить обработку
Работа с большими таблицами значений в запросах может существенно тормозить систему. Вот ключевые способы оптимизации:
- 🚀 Индексирование колонок: Если вы часто фильтруете или сортируете по определенной колонке, добавьте индекс:
ТаблицаЗначений.Индексы.Добавить("ИндексПоДатам", Новый ИндексТаблицыЗначений("ДатаДокумента")); - 🚀 Ограничение данных: Если в таблице значений миллионы строк, но нужен только фрагмент, отфильтруйте данные до передачи в запрос:
ФильтрованнаяТаблица = ТаблицаЗначений.Скопировать();ФильтрованнаяТаблица.Очистить();
Для Каждого Строка Из ТаблицаЗначений Цикл
Если Строка.Дата > НачалоГода(ТекущаяДата()) Тогда
ФильтрованнаяТаблица.Добавить(Строка);
КонецЕсли;
КонецЦикла;
- 🚀 Использование временных таблиц: Для повторяющихся операций с одной и той же таблицей значений выгрузите ее во временную таблицу базы данных (как показано в разделе 4.1).
Еще один эффективный прием — разбиение больших таблиц. Если таблица значений содержит более 50 000 строк, разбейте ее на части (например, по 10 000 строк) и обрабатывайте каждую часть отдельно. Это снизит нагрузку на память и ускорит выполнение.
Самая частая причина медленной работы — попытка обработать в одном запросе таблицу значений с сотнями тысяч строк. Всегда оценивайте объем данных и при необходимости дробите задачу на более мелкие этапы.
⚠️ Внимание: При работе с временными таблицами в 1С они автоматически удаляются после завершения сеанса. Однако в некоторых конфигурациях (например, с PostgreSQL) может потребоваться явная очистка. Проверьте настройки вашей СУБД.
6. Практические примеры: отчеты, обработки и интеграции
Рассмотрим реальные сценарии, где обработка таблиц значений в запросах незаменима.
6.1. Формирование отчета с динамическими параметрами
Допустим, нужно создать отчет по продажам с возможностью фильтрации по произвольному списку товаров, который пользователь выбирает в форме. Алгоритм:
- Пользователь выбирает товары в табличном поле формы.
- Создается таблица значений с колонками
Товар(ссылка) иНаименование(строка). - Таблица передается в запрос для фильтрации данных:
Запрос.Текст =
"ВЫБРАТЬ
| Продажи.Дата,
| Продажи.Контрагент,
| Продажи.Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг.Продажи КАК Продажи
|ГДЕ
| Продажи.Товар В (
| ВЫБРАТЬ
| Товар
| ИЗ
| &ВыбранныеТовары КАК ВыбранныеТовары
| )
|УПОРЯДОЧИТЬ ПО
| Дата";
6.2. Сравнение данных из разных источников
Частая задача — сравнить данные из 1С с данными из внешней системы (например, Excel). Например, чтобы найти расхождения в ценах:
// ТаблицаЗначенийИзExcel загружена из файла
Запрос.Текст =
"ВЫБРАТЬ
| Цены1С.Товар КАК Товар,
| Цены1С.Цена КАК Цена1С,
| ВнешниеЦены.Цена КАК ЦенаExcel,
| Цены1С.Цена - ВнешниеЦены.Цена КАК Разница
|ИЗ
| РегистрСведений.ЦеныНоменклатуры КАК Цены1С
| ЛЕВОЕ СОЕДИНЕНИЕ &ВнешниеЦены КАК ВнешниеЦены
| ПО Цены1С.Товар = ВнешниеЦены.Товар
|ГДЕ
| Цены1С.Цена <> ВнешниеЦены.Цена
| ИЛИ ВнешниеЦены.Цена ЕСТЬ NULL";
6.3. Интеграция с внешними системами
При обмене данными с API или другими программами таблицы значений часто используются как промежуточный буфер. Например, при выгрузке данных в JSON:
ТаблицаДляВыгрузки = Новый ТаблицаЗначений;
ТаблицаДляВыгрузки.Колонки.Добавить("Код");
ТаблицаДляВыгрузки.Колонки.Добавить("Наименование");
// Заполняем таблицу данными через запрос
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Код, Наименование ИЗ Справочник.Номенклатура";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТаблицаДляВыгрузки.Добавить();
НоваяСтрока.Код = Выборка.Код;
НоваяСтрока.Наименование = Выборка.Наименование;
КонецЦикла;
// Преобразуем в JSON
JSON = Новый ЗаписьJSON;
JSON.УстановитьСтроку();
ТаблицаДляВыгрузки.ЗаписатьJSON(JSON);
РезультатJSON = JSON.Закрыть();
7. Альтернативные подходы: когда не стоит использовать таблицы значений в запросах
Несмотря на гибкость, таблицы значений в запросах не всегда оптимальны. Рассмотрим случаи, когда лучше использовать другие техники:
- 🛑 Очень большие объемы данных (100 000+ строк): лучше работать напрямую с базой или использовать временные таблицы.
- 🛑 Сложные многоуровневые отчеты: для них подойдут системы компоновки данных (СКД).
- 🛑 Регулярные операции: если данные часто обновляются, лучше хранить их в регистрах сведений или справочниках.
- 🛑 Транзакционные операции: таблицы значений не поддерживают откат изменений.
Например, для построения аналитического отчета с группировками по нескольким измерениям эффективнее использовать СКД:
СхемаКомпоновкиДанных = ПолучаемСхемуКомпоновкиДанных();
НастройкиКомпоновки = ПолучаемНастройкиКомпоновки();
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
РезультатКомпоновки = ПроцессорКомпоновки.Выполнить();
Еще один пример — работа с иерархическими данными (например, дерево справочника). В этом случае лучше использовать рекурсивные запросы или специализированные методы обхода дерева, чем пытаться свести все в таблицу значений.
⚠️ Внимание: В некоторых версиях 1С:Предприятие (до 8.3.10) были ограничения на размер таблиц значений, передаваемых в запросы (около 65 000 строк). В современных версиях это ограничение снято, но производительность все равно падает при работе с очень большими наборами данных.
8. Отладка и диагностика: как найти ошибки в запросах с таблицами значений
Ошибки в запросах с таблицами значений часто сложно диагностировать из-за динамической природы данных. Вот алгоритм поиска проблем:
8.1. Проверка структуры таблицы значений
Перед выполнением запроса убедитесь, что таблица значений содержит ожидаемые колонки и данные:
Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
Сообщить("Колонка: " + Колонка.Имя + ", Тип: " + ТипЗнч(Колонка.Значение));
КонецЦикла;
8.2. Использование конструктора запросов
Если запрос возвращает неожиданный результат, воспользуйтесь конструктором запросов в конфигураторе:
- Откройте окно запроса (
Файл → Новый → Запрос). - Вставьте текст вашего запроса.
- Нажмите
Конструктор запросаи проверьте визуальное представление. - Используйте кнопку
Выполнитьдля тестирования.
8.3. Логирование промежуточных результатов
Для сложных запросов полезно разбить их на части и логировать промежуточные результаты:
// Шаг 1: Получаем исходные данные
Запрос1 = Новый Запрос("ВЫБРАТЬ * ИЗ &Таблица1");
Запрос1.УстановитьПараметр("Таблица1", Таблица1);
Результат1 = Запрос1.Выполнить();
ТаблицаРезультатов1 = Результат1.Выгрузить();
// Шаг 2: Обрабатываем данные
Запрос2 = Новый Запрос("ВЫБРАТЬ ... ИЗ &ТаблицаРезультатов1 ...");
Запрос2.УстановитьПараметр("ТаблицаРезультатов1", ТаблицаРезультатов1);
Результат2 = Запрос2.Выполнить();
8.4. Анализ плана выполнения запроса
Для диагностики производительности используйте план выполнения запроса:
ОбъяснениеЗапроса = Запрос.Выполнить().ПолучитьОбъяснениеЗапроса();
ОбъяснениеЗапроса.Вывести();
Это поможет выявить "узкие места" — например, полное сканирование таблиц вместо использования индексов.
Если запрос с таблицей значений выполняется дольше 5 секунд, скорее всего, проблема в отсутствии индексов или избыточном объеме данных. Всегда проверяйте размер таблицы значений перед передачей в запрос.
FAQ: Частые вопросы по обработке таблиц значений в запросах 1С
Можно ли в таблице значений использовать колонки с типом "Таблица значений"?
Да, но с оговорками. Колонка может содержать ссылку на другую таблицу значений, но в запросе 1С не сможет развернуть ее автоматически. Для работы с вложенными таблицами потребуется обходить их в цикле или использовать временные таблицы.
Как передать в запрос таблицу значений с динамическим набором колонок?
Динамический набор колонок в таблице значений не поддерживается напрямую в запросах. Вам нужно либо заранее знать структуру таблицы, либо формировать текст запроса динамически с учетом фактических колонок:
ТекстЗапроса = "ВЫБРАТЬ ";
Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
ТекстЗапроса = ТекстЗапроса + Колонка.Имя + ", ";
КонецЦикла;
ТекстЗапроса = Лев(ТекстЗапроса, СтрДлина(ТекстЗапроса) - 2) + " ИЗ &Таблица";
Почему запрос с таблицей значений работает медленнее, чем аналогичный запрос к базе данных?
Причины могут быть следующими:
- Отсутствие индексов в таблице значений (в отличие от таблиц базы данных).
- Накладные расходы на передачу данных между 1С и СУБД.
- Неоптимальная структура таблицы значений (например, избыточные колонки).
Для ускорения попробуйте:
- Добавить индексы на часто используемые колонки.
- Уменьшить количество строк и колонок в таблице значений.
- Использовать временные таблицы базы данных.
Можно ли обновить данные в таблице значений через запрос?
Нет, в 1С нельзя напрямую выполнять ОБНОВИТЬ или УДАЛИТЬ для таблиц значений в запросах. Для изменения данных нужно:
- Получить результат запроса в новую таблицу значений.
- Обработать ее в цикле или с помощью методов таблицы значений (
НайтиСтроки(),Получить()и т.д.). - Обновить исходную таблицу значений на основе изменений.
Как передать в запрос таблицу значений с иерархическими данными?
Для иерархических данных (например, дерево справочника) лучше не использовать таблицы значений в запросах. Вместо этого:
- Используйте рекурсивные запросы (если СУБД поддерживает).
- Применяйте методы обхода дерева (
ПолучитьСсылкуНаРодителя(),ЭтоГруппа()). - Для выгрузки иерархии в таблицу значений используйте циклы с рекурсией.
Пример рекурсивного обхода:
Процедура ОбойтиДерево(Элемент, ТаблицаРезультатов, Уровень = 0)
НоваяСтрока = ТаблицаРезультатов.Добавить();
НоваяСтрока.Элемент = Элемент;
НоваяСтрока.Уровень = Уровень;
Если Элемент.ЭтоГруппа() Тогда
Для Каждого Подчиненный Из Элемент.Подчиненные Цикл
ОбойтиДерево(Подчиненный, ТаблицаРезультатов, Уровень + 1);
КонецЦикла;
КонецЕсли;
КонецПроцедуры