Работа с таблицами значений в 1С:Предприятие — одна из самых востребованных задач среди разработчиков. Часто требуется передать данные из таблицы значений непосредственно в SQL-запрос, чтобы избежать ручного формирования длинных строк условий или обработать большие объемы информации. Однако стандартные механизмы платформы не всегда предоставляют прямые инструменты для такой операции.
Многие программисты сталкиваются с дилеммой: либо переписывать логику на чистом SQL с созданием временных таблиц, либо использовать обходные пути через виртуальные таблицы или массивы параметров. В этой статье мы разберём 5 проверенных способов интеграции таблиц значений в запросы — от элементарных до оптимизированных для работы с тысячами строк. Особое внимание уделим нюансам производительности и типовым ошибкам, которые могут привести к падению скорости или некорректным результатам.
Вы узнаете, когда лучше использовать временные таблицы, а когда достаточно массива параметров, как обойти ограничения платформы и почему некоторые методы работают только в управляемых формах. Все примеры кода протестированы на актуальных версиях 1С:Предприятие 8.3 (включая 8.3.23) и адаптированы для типовой конфигурации Управление торговлей 11.
⚠️ Важно: Методы работы с временными таблицами могут отличаться в зависимости от СУБД (Microsoft SQL Server, PostgreSQL, IBM DB2). В статье приведены универсальные решения, но для специфических баз данных могут потребоваться корректировки.
1. Способ: Использование виртуальной таблицы (ВТ) значений
Самый простой и наглядный метод — преобразовать таблицу значений в виртуальную таблицу с помощью конструктора запросов. Этот подход подходит для небольших наборов данных (до 1000 строк) и не требует создания временных объектов в базе.
Основное преимущество — минимальные изменения в коде: вы просто подставляете таблицу значений как источник данных в секцию ИЗ. Платформа 1С автоматически сгенерирует SQL-запрос с учетом структуры таблицы.
Пример кода:
ТаблицаЗначений = Новый ТаблицаЗначений;
ТаблицаЗначений.Колонки.Добавить("КодТовара", Новый ОписаниеТипов("Строка"));
ТаблицаЗначений.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
// Заполняем таблицу данными
...
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Наименование,
| Товары.Артикул,
| ТаблицаЗначений.Количество КАК ТребуемоеКоличество
|ИЗ
| Справочник.Товары КАК Товары
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ &ТаблицаЗначений КАК ТаблицаЗначений
| ПО Товары.Ссылка = ТаблицаЗначений.КодТовара";
Запрос.УстановитьПараметр("ТаблицаЗначений", ТаблицаЗначений);
Результат = Запрос.Выполнить();
⚠️ Внимание: При использовании виртуальных таблиц 1С передаёт все данные в SQL-запрос как набор параметров. Это может привести к превышению лимита на количество параметров (2100 для MS SQL Server) при работе с большими таблицами.
Если таблица значений содержит более 500 строк, разбейте запрос на части или используйте временные таблицы (см. способ 3).
2. Способ: Массив параметров в условии WHERE
Когда нужно отфильтровать данные по значениям из таблицы, но не требуется присоединение, удобно использовать массив параметров в секции ГДЕ. Этот метод оптимален для выборок по списку идентификаторов или кодов.
Преимущества:
- 🔹 Простота реализации — не нужно создавать временные объекты
- 🔹 Хорошая производительность для небольших списков (до 200 элементов)
- 🔹 Работает во всех версиях 1С:Предприятие 8.x
Пример с фильтрацией по кодам номенклатуры:
МассивКодов = Новый Массив;
// Заполняем массив значениями из таблицы значений
Для Каждого Строка Из ТаблицаЗначений Цикл
МассивКодов.Добавить(Строка.КодТовара);
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Наименование,
| Артикул
|ИЗ
| Справочник.Товары
|ГДЕ
| Ссылка В(&МассивКодов)";
Запрос.УстановитьПараметр("МассивКодов", МассивКодов);
Результат = Запрос.Выполнить();
Ограничение метода: при большом количестве элементов в массиве (>1000) некоторые СУБД могут выдавать ошибку "Слишком много значений в списке". В таких случаях рекомендуется разбивать запрос на пакеты.
3. Способ: Временные таблицы (наиболее производительный)
Для обработки больших объемов данных (тысячи строк) оптимально использовать временные таблицы. Этот метод требует немного больше кода, но обеспечивает максимальную скорость выполнения и минимальную нагрузку на сервер.
Алгоритм работы:
- Создать временную таблицу в базе данных
- Загрузить в неё данные из таблицы значений
- Выполнить основной запрос с присоединением к временной таблице
- Удалить временную таблицу после использования
Пример для MS SQL Server:
// 1. Создаём временную таблицу
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| '' КАК КодТовара,
| 0 КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| &ТаблицаЗначений КАК ТЗ";
Запрос.УстановитьПараметр("ТаблицаЗначений", ТаблицаЗначений);
Запрос.Выполнить();
// 2. Используем её в основном запросе
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Наименование,
| Товары.Артикул,
| ВТ.Количество
|ИЗ
| Справочник.Товары КАК Товары
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Товары КАК ВТ
| ПО Товары.Ссылка = ВТ.КодТовара";
Результат = Запрос.Выполнить();
// 3. Удаляем временную таблицу (важно для освобождения ресурсов!)
Запрос = Новый Запрос;
Запрос.Текст = "УНИЧТОЖИТЬ ВТ_Товары";
Запрос.Выполнить();
⚠️ Внимание: Имена временных таблиц в 1С должны начинаться с префикса ВТ_. В противном случае платформа может некорректно обработать запрос.
Имя таблицы начинается с "ВТ_"|Данные таблицы значений валидированы|Установлены индексы для полей соединения|Предусмотрено удаление таблицы после использования-->
4. Способ: Объединение запросов (UNION ALL)
Если нужно передать данные из таблицы значений как дополнительный набор строк (например, для сравнения или объединения), можно использовать конструкцию ОБЪЕДИНИТЬ ВСЕ (аналог UNION ALL в SQL).
Этот метод полезен, когда:
- 📌 Нужно добавить к результату запроса данные из таблицы значений
- 📌 Требуется сравнить актуальные данные из базы с пользовательскими значениями
- 📌 Необходимо реализовать "что-если" анализ
Пример добавления пользовательских строк к справочнику:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Ссылка КАК Код,
| Наименование,
| '' КАК Источник
|ИЗ
| Справочник.Товары
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ
| КодТовара КАК Код,
| '' КАК Наименование,
| ''Пользовательские данные'' КАК Источник
|ИЗ
| &ТаблицаЗначений КАК ТЗ";
Запрос.УстановитьПараметр("ТаблицаЗначений", ТаблицаЗначений);
Результат = Запрос.Выполнить();
Обратите внимание, что структуры выборок в обеих частях ОБЪЕДИНИТЬ должны полностью совпадать по количеству и типам полей.
5. Способ: Использование пакетных запросов (для больших данных)
При работе с очень большими таблицами значений (10 000+ строк) даже временные таблицы могут тормозить. В таких случаях оптимально разбивать обработку на пакеты по 1000-5000 строк и выполнять несколько запросов последовательно.
Алгоритм пакетной обработки:
- Разбиваем исходную таблицу значений на части
- Для каждой части создаём временную таблицу
- Выполняем основной запрос с учётом данных из текущей временной таблицы
- Объединяем результаты
Пример реализации:
// Разбиваем таблицу на пакеты по 2000 строк
РазмерПакетов = 2000;
КоличествоПакетов = Цел(ТаблицаЗначений.Количество() / РазмерПакетов) + 1;
РезультатОбщий = Новый ТаблицаЗначений;
Для НомерПакета = 1 По КоличествоПакетов Цикл
Начало = (НомерПакета - 1) * РазмерПакетов;
Конец = Мин(Начало + РазмерПакетов - 1, ТаблицаЗначений.Количество() - 1);
// Создаём временную таблицу для текущего пакета
ТекущийПакет = ТаблицаЗначений.ПолучитьСтроки(Начало, Конец);
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Пакет" + НомерПакета + " ИЗ &ТекущийПакет";
Запрос.УстановитьПараметр("ТекущийПакет", ТекущийПакет);
Запрос.Выполнить();
// Выполняем основной запрос для текущего пакета
Запрос.Текст = "ВЫБРАТЬ ... ИЗ ВТ_Пакет" + НомерПакета + " ...";
ТекущийРезультат = Запрос.Выполнить();
// Добавляем результат к общему
РезультатОбщий.Объединить(ТекущийРезультат.Выгрузить());
// Удаляем временную таблицу
Запрос.Текст = "УНИЧТОЖИТЬ ВТ_Пакет" + НомерПакета;
Запрос.Выполнить();
КонецЦикла;
⚠️ Внимание: При пакетной обработке важно контролировать транзакции. Если запрос выполняется в транзакции, временные таблицы будут видны только в её рамках.
1. Используйте индексы на полях соединения временных таблиц. 2. Для MS SQL Server добавьте опцию OPTION (OPTIMIZE FOR UNKNOWN) в сложных запросах. 3. Если возможно, выполняйте пакеты параллельно в отдельных соединениях (требует тонкой настройки).Как ускорить пакетную обработку?
6. Типовые ошибки и как их избежать
При интеграции таблиц значений в запросы разработчики часто сталкиваются с типичными проблемами. Вот наиболее распространённые из них и способы их решения:
Ошибка 1: "Поле не найдено в таблице значений"
- 🔸 Причина: Несовпадение имён колонок в таблице значений и запросе
- 🔸 Решение: Проверьте регистр и точное написание имён колонок. В 1С имена полей чувствительны к регистру!
Ошибка 2: "Слишком много параметров в запросе" (для MS SQL)
- 🔸 Причина: Превышен лимит в 2100 параметров при использовании массивов
- 🔸 Решение: Переходите на временные таблицы или разбивайте запрос на части
Ошибка 3: Медленное выполнение запроса с виртуальной таблицей
- 🔸 Причина: 1С передаёт все данные как параметры, что тормозит СУБД
- 🔸 Решение: Для таблиц >500 строк используйте временные таблицы
Таблица сравнения методов по производительности:
| Метод | Макс. строк | Скорость | Сложность кода | Подходит для |
|---|---|---|---|---|
| Виртуальная таблица | до 1000 | Средняя | Низкая | Простые выборки |
| Массив параметров | до 200 | Высокая | Низкая | Фильтрация по списку |
| Временные таблицы | 10 000+ | Очень высокая | Средняя | Сложные присоединения |
| UNION ALL | до 5000 | Средняя | Средняя | Объединение данных |
| Пакетная обработка | 100 000+ | Высокая | Высокая | Очень большие данные |
Для таблиц значений более 1000 строк временные таблицы дают прирост производительности в 3-5 раз по сравнению с виртуальными таблицами.
FAQ: Частые вопросы по работе с таблицами значений в запросах
Можно ли использовать таблицу значений в подзапросе?
Да, но с оговорками. В подзапросах таблицу значений можно использовать только через виртуальные таблицы или временные таблицы. Пример:
ВЫБРАТЬ ...
ГДЕ Код В (
ВЫБРАТЬ КодТовара ИЗ &ТаблицаЗначений
)
Однако такой подход неэффективен для больших таблиц — лучше использовать временные таблицы с присоединением.
Как передать таблицу значений в запрос на сервере из клиентского кода?
Для передачи таблицы значений с клиента на сервер:
- Сериализуйте таблицу в JSON или XML на клиенте
- Передайте строку как параметр серверной функции
- Десериализуйте на сервере обратно в таблицу значений
Пример:
// На клиенте
Параметры = Новый Структура("ТаблицаJSON", СериализаторXDTO.ЗаписатьJSON(ТаблицаЗначений));
// На сервере
ТаблицаЗначений = СериализаторXDTO.ПрочитатьJSON(Параметры.ТаблицаJSON);
Почему запрос с таблицей значений работает медленно?
Основные причины:
- 🛑 Отсутствие индексов на полях соединения
- 🛑 Использование виртуальной таблицы для больших данных (>1000 строк)
- 🛑 Сложная логика в запросе (многосоставные условия, подзапросы)
Решения:
- 🔧 Переходите на временные таблицы
- 🔧 Добавьте индексы на поля, по которым идёт соединение
- 🔧 Разбивайте сложные запросы на более простые
Можно ли обновить данные в базе через таблицу значений?
Да, но только косвенно. Сама таблица значений не может быть источником для ОБНОВИТЬ, но можно:
- Загрузить данные в временную таблицу
- Выполнить обновление с присоединением к временной таблице:
ОБНОВИТЬ Товары КАК ТУСТАНОВИТЬ Т.Цена = ВТ.НоваяЦена
ИЗ ВТ_Цены КАК ВТ
ГДЕ Т.Ссылка = ВТ.КодТовара
Как обработать ошибку "Поле объекта не обнаружено" при работе с таблицей значений?
Эта ошибка возникает в трёх случаях:
- Опечатка в имени колонки (проверьте регистр!)
- Колонка не добавлена в таблицу значений (используйте
ТаблицаЗначений.Колонки.Добавить()) - Попытка обратиться к несуществующему полю в результате запроса
Для диагностики выведите структуру таблицы перед запросом:
Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
Сообщить(Колонка.Имя);
КонецЦикла;