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

Многие программисты сталкиваются с дилеммой: либо переписывать логику на чистом SQL с созданием временных таблиц, либо использовать обходные пути через виртуальные таблицы или массивы параметров. В этой статье мы разберём 5 проверенных способов интеграции таблиц значений в запросы — от элементарных до оптимизированных для работы с тысячами строк. Особое внимание уделим нюансам производительности и типовым ошибкам, которые могут привести к падению скорости или некорректным результатам.

Вы узнаете, когда лучше использовать временные таблицы, а когда достаточно массива параметров, как обойти ограничения платформы и почему некоторые методы работают только в управляемых формах. Все примеры кода протестированы на актуальных версиях 1С:Предприятие 8.3 (включая 8.3.23) и адаптированы для типовой конфигурации Управление торговлей 11.

⚠️ Важно: Методы работы с временными таблицами могут отличаться в зависимости от СУБД (Microsoft SQL Server, PostgreSQL, IBM DB2). В статье приведены универсальные решения, но для специфических баз данных могут потребоваться корректировки.

1. Способ: Использование виртуальной таблицы (ВТ) значений

Самый простой и наглядный метод — преобразовать таблицу значений в виртуальную таблицу с помощью конструктора запросов. Этот подход подходит для небольших наборов данных (до 1000 строк) и не требует создания временных объектов в базе.

Основное преимущество — минимальные изменения в коде: вы просто подставляете таблицу значений как источник данных в секцию ИЗ. Платформа автоматически сгенерирует SQL-запрос с учетом структуры таблицы.

Пример кода:

ТаблицаЗначений = Новый ТаблицаЗначений;

ТаблицаЗначений.Колонки.Добавить("КодТовара", Новый ОписаниеТипов("Строка"));

ТаблицаЗначений.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));

// Заполняем таблицу данными

...

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

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

"ВЫБРАТЬ

| Товары.Наименование,

| Товары.Артикул,

| ТаблицаЗначений.Количество КАК ТребуемоеКоличество

|ИЗ

| Справочник.Товары КАК Товары

| ВНУТРЕННЕЕ СОЕДИНЕНИЕ &ТаблицаЗначений КАК ТаблицаЗначений

| ПО Товары.Ссылка = ТаблицаЗначений.КодТовара";

Запрос.УстановитьПараметр("ТаблицаЗначений", ТаблицаЗначений);

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

⚠️ Внимание: При использовании виртуальных таблиц передаёт все данные в SQL-запрос как набор параметров. Это может привести к превышению лимита на количество параметров (2100 для MS SQL Server) при работе с большими таблицами.

💡

Если таблица значений содержит более 500 строк, разбейте запрос на части или используйте временные таблицы (см. способ 3).

2. Способ: Массив параметров в условии WHERE

Когда нужно отфильтровать данные по значениям из таблицы, но не требуется присоединение, удобно использовать массив параметров в секции ГДЕ. Этот метод оптимален для выборок по списку идентификаторов или кодов.

Преимущества:

  • 🔹 Простота реализации — не нужно создавать временные объекты
  • 🔹 Хорошая производительность для небольших списков (до 200 элементов)
  • 🔹 Работает во всех версиях 1С:Предприятие 8.x

Пример с фильтрацией по кодам номенклатуры:

МассивКодов = Новый Массив;

// Заполняем массив значениями из таблицы значений

Для Каждого Строка Из ТаблицаЗначений Цикл

МассивКодов.Добавить(Строка.КодТовара);

КонецЦикла;

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

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

"ВЫБРАТЬ

| Наименование,

| Артикул

|ИЗ

| Справочник.Товары

|ГДЕ

| Ссылка В(&МассивКодов)";

Запрос.УстановитьПараметр("МассивКодов", МассивКодов);

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

Ограничение метода: при большом количестве элементов в массиве (>1000) некоторые СУБД могут выдавать ошибку "Слишком много значений в списке". В таких случаях рекомендуется разбивать запрос на пакеты.

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

3. Способ: Временные таблицы (наиболее производительный)

Для обработки больших объемов данных (тысячи строк) оптимально использовать временные таблицы. Этот метод требует немного больше кода, но обеспечивает максимальную скорость выполнения и минимальную нагрузку на сервер.

Алгоритм работы:

  1. Создать временную таблицу в базе данных
  2. Загрузить в неё данные из таблицы значений
  3. Выполнить основной запрос с присоединением к временной таблице
  4. Удалить временную таблицу после использования

Пример для MS SQL Server:

// 1. Создаём временную таблицу

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

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

"ВЫБРАТЬ

| '' КАК КодТовара,

| 0 КАК Количество

|ПОМЕСТИТЬ ВТ_Товары

|ИЗ

| &ТаблицаЗначений КАК ТЗ";

Запрос.УстановитьПараметр("ТаблицаЗначений", ТаблицаЗначений);

Запрос.Выполнить();

// 2. Используем её в основном запросе

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

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

"ВЫБРАТЬ

| Товары.Наименование,

| Товары.Артикул,

| ВТ.Количество

|ИЗ

| Справочник.Товары КАК Товары

| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Товары КАК ВТ

| ПО Товары.Ссылка = ВТ.КодТовара";

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

// 3. Удаляем временную таблицу (важно для освобождения ресурсов!)

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

Запрос.Текст = "УНИЧТОЖИТЬ ВТ_Товары";

Запрос.Выполнить();

⚠️ Внимание: Имена временных таблиц в должны начинаться с префикса ВТ_. В противном случае платформа может некорректно обработать запрос.

Имя таблицы начинается с "ВТ_"|Данные таблицы значений валидированы|Установлены индексы для полей соединения|Предусмотрено удаление таблицы после использования-->

4. Способ: Объединение запросов (UNION ALL)

Если нужно передать данные из таблицы значений как дополнительный набор строк (например, для сравнения или объединения), можно использовать конструкцию ОБЪЕДИНИТЬ ВСЕ (аналог UNION ALL в SQL).

Этот метод полезен, когда:

  • 📌 Нужно добавить к результату запроса данные из таблицы значений
  • 📌 Требуется сравнить актуальные данные из базы с пользовательскими значениями
  • 📌 Необходимо реализовать "что-если" анализ

Пример добавления пользовательских строк к справочнику:

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

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

"ВЫБРАТЬ

| Ссылка КАК Код,

| Наименование,

| '' КАК Источник

|ИЗ

| Справочник.Товары

|ОБЪЕДИНИТЬ ВСЕ

|ВЫБРАТЬ

| КодТовара КАК Код,

| '' КАК Наименование,

| ''Пользовательские данные'' КАК Источник

|ИЗ

| &ТаблицаЗначений КАК ТЗ";

Запрос.УстановитьПараметр("ТаблицаЗначений", ТаблицаЗначений);

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

Обратите внимание, что структуры выборок в обеих частях ОБЪЕДИНИТЬ должны полностью совпадать по количеству и типам полей.

5. Способ: Использование пакетных запросов (для больших данных)

При работе с очень большими таблицами значений (10 000+ строк) даже временные таблицы могут тормозить. В таких случаях оптимально разбивать обработку на пакеты по 1000-5000 строк и выполнять несколько запросов последовательно.

Алгоритм пакетной обработки:

  1. Разбиваем исходную таблицу значений на части
  2. Для каждой части создаём временную таблицу
  3. Выполняем основной запрос с учётом данных из текущей временной таблицы
  4. Объединяем результаты

Пример реализации:

// Разбиваем таблицу на пакеты по 2000 строк

РазмерПакетов = 2000;

КоличествоПакетов = Цел(ТаблицаЗначений.Количество() / РазмерПакетов) + 1;

РезультатОбщий = Новый ТаблицаЗначений;

Для НомерПакета = 1 По КоличествоПакетов Цикл

Начало = (НомерПакета - 1) * РазмерПакетов;

Конец = Мин(Начало + РазмерПакетов - 1, ТаблицаЗначений.Количество() - 1);

// Создаём временную таблицу для текущего пакета

ТекущийПакет = ТаблицаЗначений.ПолучитьСтроки(Начало, Конец);

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

Запрос.Текст = "ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Пакет" + НомерПакета + " ИЗ &ТекущийПакет";

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

Запрос.Выполнить();

// Выполняем основной запрос для текущего пакета

Запрос.Текст = "ВЫБРАТЬ ... ИЗ ВТ_Пакет" + НомерПакета + " ...";

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

// Добавляем результат к общему

РезультатОбщий.Объединить(ТекущийРезультат.Выгрузить());

// Удаляем временную таблицу

Запрос.Текст = "УНИЧТОЖИТЬ ВТ_Пакет" + НомерПакета;

Запрос.Выполнить();

КонецЦикла;

⚠️ Внимание: При пакетной обработке важно контролировать транзакции. Если запрос выполняется в транзакции, временные таблицы будут видны только в её рамках.

Как ускорить пакетную обработку?

1. Используйте индексы на полях соединения временных таблиц.

2. Для MS SQL Server добавьте опцию OPTION (OPTIMIZE FOR UNKNOWN) в сложных запросах.

3. Если возможно, выполняйте пакеты параллельно в отдельных соединениях (требует тонкой настройки).

6. Типовые ошибки и как их избежать

При интеграции таблиц значений в запросы разработчики часто сталкиваются с типичными проблемами. Вот наиболее распространённые из них и способы их решения:

Ошибка 1: "Поле не найдено в таблице значений"

  • 🔸 Причина: Несовпадение имён колонок в таблице значений и запросе
  • 🔸 Решение: Проверьте регистр и точное написание имён колонок. В имена полей чувствительны к регистру!

Ошибка 2: "Слишком много параметров в запросе" (для MS SQL)

  • 🔸 Причина: Превышен лимит в 2100 параметров при использовании массивов
  • 🔸 Решение: Переходите на временные таблицы или разбивайте запрос на части

Ошибка 3: Медленное выполнение запроса с виртуальной таблицей

  • 🔸 Причина: передаёт все данные как параметры, что тормозит СУБД
  • 🔸 Решение: Для таблиц >500 строк используйте временные таблицы

Таблица сравнения методов по производительности:

Метод Макс. строк Скорость Сложность кода Подходит для
Виртуальная таблица до 1000 Средняя Низкая Простые выборки
Массив параметров до 200 Высокая Низкая Фильтрация по списку
Временные таблицы 10 000+ Очень высокая Средняя Сложные присоединения
UNION ALL до 5000 Средняя Средняя Объединение данных
Пакетная обработка 100 000+ Высокая Высокая Очень большие данные

💡

Для таблиц значений более 1000 строк временные таблицы дают прирост производительности в 3-5 раз по сравнению с виртуальными таблицами.

FAQ: Частые вопросы по работе с таблицами значений в запросах

Можно ли использовать таблицу значений в подзапросе?

Да, но с оговорками. В подзапросах таблицу значений можно использовать только через виртуальные таблицы или временные таблицы. Пример:

ВЫБРАТЬ ...

ГДЕ Код В (

ВЫБРАТЬ КодТовара ИЗ &ТаблицаЗначений

)

Однако такой подход неэффективен для больших таблиц — лучше использовать временные таблицы с присоединением.

Как передать таблицу значений в запрос на сервере из клиентского кода?

Для передачи таблицы значений с клиента на сервер:

  1. Сериализуйте таблицу в JSON или XML на клиенте
  2. Передайте строку как параметр серверной функции
  3. Десериализуйте на сервере обратно в таблицу значений

Пример:

// На клиенте

Параметры = Новый Структура("ТаблицаJSON", СериализаторXDTO.ЗаписатьJSON(ТаблицаЗначений));

// На сервере

ТаблицаЗначений = СериализаторXDTO.ПрочитатьJSON(Параметры.ТаблицаJSON);

Почему запрос с таблицей значений работает медленно?

Основные причины:

  • 🛑 Отсутствие индексов на полях соединения
  • 🛑 Использование виртуальной таблицы для больших данных (>1000 строк)
  • 🛑 Сложная логика в запросе (многосоставные условия, подзапросы)

Решения:

  • 🔧 Переходите на временные таблицы
  • 🔧 Добавьте индексы на поля, по которым идёт соединение
  • 🔧 Разбивайте сложные запросы на более простые
Можно ли обновить данные в базе через таблицу значений?

Да, но только косвенно. Сама таблица значений не может быть источником для ОБНОВИТЬ, но можно:

  1. Загрузить данные в временную таблицу
  2. Выполнить обновление с присоединением к временной таблице:
    ОБНОВИТЬ Товары КАК Т
    

    УСТАНОВИТЬ Т.Цена = ВТ.НоваяЦена

    ИЗ ВТ_Цены КАК ВТ

    ГДЕ Т.Ссылка = ВТ.КодТовара

Как обработать ошибку "Поле объекта не обнаружено" при работе с таблицей значений?

Эта ошибка возникает в трёх случаях:

  1. Опечатка в имени колонки (проверьте регистр!)
  2. Колонка не добавлена в таблицу значений (используйте ТаблицаЗначений.Колонки.Добавить())
  3. Попытка обратиться к несуществующему полю в результате запроса

Для диагностики выведите структуру таблицы перед запросом:

Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл

Сообщить(Колонка.Имя);

КонецЦикла;