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

В этой статье мы разберём не только базовый синтаксис работы с таблицами значений в запросах, но и продвинутые техники: параметризацию, временные таблицы, оптимизацию для больших объёмов данных. Особое внимание уделим практическим примерам с пояснениями, которые помогут избежать распространённых ошибок при работе с ВЫБРАТЬ, ГДЕ и СОЕДИНИТЬ. Материал будет полезен как начинающим программистам , так и опытным специалистам, которые хотят систематизировать свои знания.

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

Что такое таблица значений в контексте запросов 1С

Таблица значений (ТЗ) в 1С:Предприятие — это динамическая структура данных, которая хранит информацию в виде строк и колонок. В отличие от обычных массивов, она позволяет работать с данными как с таблицей базы данных: добавлять колонки, сортировать, фильтровать и даже выполнять SQL-подобные операции. Но как она соотносится с запросами?

В запросах таблица значений может использоваться в трёх основных ролях:

  • 📥 Источник данных — когда ТЗ подставляется в запрос как временная таблица (через ВЫБРАТЬ ИЗ &Таблица).
  • 🔍 Параметр отбора — когда значения из ТЗ используются в условиях ГДЕ (например, ГДЕ Ссылка В (&СписокСсылок)).
  • 🔄 Результат запроса — когда данные из запроса выгружаются в ТЗ для дальнейшей обработки.

Главное отличие от обычных таблиц базы данных — таблица значений существует только в оперативной памяти во время выполнения кода. Это даёт гибкость, но накладывает ограничения: например, нельзя использовать ТЗ в запросах, которые выполняются на сервере без передачи контекста (как в отчётах с серверным выполнением).

📊 Как часто вы используете таблицы значений в запросах 1С?
Постоянно, в каждом проекте
Иногда, для специфических задач
Рядом, но предпочитаю другие методы
Никогда не использовал

Базовый синтаксис: как передать таблицу значений в запрос

Самый простой способ использовать ТЗ в запросе — передать её как параметр с помощью символа &. Рассмотрим минимальный рабочий пример:

// Создаём таблицу значений с данными

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

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

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

НоваяСтрока = ТаблицаТоваров.Добавить;

НоваяСтрока.Ссылка = Справочники.Номенклатура.НайтиПоНаименованию("Монитор 24\"");

НоваяСтрока.Количество = 5;

// Формируем запрос с использованием ТЗ

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

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

"ВЫБРАТЬ

| ТоварыНаСкладах.Номенклатура КАК Номенклатура,

| ТоварыНаСкладах.КоличествоОстаток КАК Остаток

|ИЗ

| РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах

|ГДЕ

| ТоварыНаСкладах.Номенклатура В (&ТаблицаТоваров.Ссылка)";

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

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

Обратите внимание на ключевые моменты:

  • 🔹 В тексте запроса указываем &ТаблицаТоваров.Ссылка — это означает, что мы берём колонку Ссылка из переданной ТЗ.
  • 🔹 Метод УстановитьПараметр связывает имя параметра в запросе (&ТаблицаТоваров) с реальной таблицей значений.
  • 🔹 В условии ГДЕ используется оператор В, который проверяет вхождение значения в список.

Такой подход работает для простых случаев, но имеет ограничение: если в ТЗ много строк, запрос может выполняться медленно из-за преобразования данных. Для оптимизации используют временные таблицы — о них поговорим далее.

💡

Всегда проверяйте типы данных колонок таблицы значений перед передачей в запрос. Например, если в ТЗ колонка имеет тип"Строка", а в запросе сравнивается с полем типа"Число", возникнет ошибка несоответствия типов.

Временные таблицы: ускорение работы с большими данными

Когда таблица значений содержит сотни или тысячи строк, прямой подстановки в запрос через параметр недостаточно — это приводит к длительному парсингу и замедлению выполнения. В таких случаях используют временные таблицы, которые создаются на сервере и существуют только во время сеанса.

Пример использования временной таблицы:

// Создаём таблицу значений с большим объёмом данных

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

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

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

//.. заполняем таблицу данными (например, 1000 строк)

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

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

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

"ВЫБРАТЬ

| &ТаблицаЗаказов.Дата КАК Дата,

| &ТаблицаЗаказов.Сумма КАК Сумма

|ПОМЕСТИТЬ ВТ_Заказы"; // ВТ_ — префикс для временной таблицы

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

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

// Теперь используем временную таблицу в основном запросе

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

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

"ВЫБРАТЬ

| ВТ_Заказы.Дата КАК Дата,

| СУММА(ВТ_Заказы.Сумма) КАК Итого

|ИЗ

| ВТ_Заказы КАК ВТ_Заказы

|СГРУППИРОВАТЬ ПО

| ВТ_Заказы.Дата";

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

Преимущества временных таблиц:

  • Производительность: данные один раз загружаются на сервер и дальше обрабатываются как обычная таблица БД.
  • 🔄 Многоразовое использование: одну временную таблицу можно использовать в нескольких запросах.
  • 🛠 Гибкость: поддерживаются индексы, соединения (СОЕДИНИТЬ), агрегатные функции.

Создать таблицу значений с нужной структурой колонок|

Заполнить таблицу данными (не менее 100 строк для заметного эффекта)|

Проверить типы данных колонок на соответствие полям в запросе|

Использовать префикс ВТ_ для имени временной таблицы|

Убедиться, что запрос выполняется на сервере (для клиент-серверного варианта)

-->

Если вам нужно сохранить данные дольше, рассмотрите вариант записи во временное хранилище (ПоместитьВоВременноеХранилище).

Параметризация запросов: как избежать ошибок с типами данных

Одна из самых распространённых ошибок при работе с таблицами значений в запросах — несоответствие типов данных. Например, если в ТЗ колонка имеет тип Строка, а в запросе сравнивается с полем типа Число, платформа выдаст ошибку. Чтобы этого избежать, нужно явно указывать типы при создании ТЗ.

Рассмотрим типичные проблемы и их решения:

Проблема Причина Решение
Ошибка"Тип не совпадает" В ТЗ колонка типа Строка, а в запросе сравнивается с Дата Использовать ОписаниеТипов("Дата") при добавлении колонки
Пустые результаты запроса В ТЗ передаются NULL-значения, которые игнорируются в условии ГДЕ Добавить проверку на ЗНАЧЕНИЕ ЗАПОЛНЕНО в запрос
Медленное выполнение В ТЗ слишком много строк, и они передаются как параметр, а не временная таблица Использовать конструкцию ПОМЕСТИТЬ ВТ_..

Пример корректной параметризации с учётом типов:

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

ТаблицаДат.Колонки.Добавить("Период",

Новый ОписаниеТипов("Дата", Новый КвалификаторыЧисла(10, 0))); // Явное указание типа

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

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

"ВЫБРАТЬ

| Документ.Дата КАК ДатаДокумента

|ИЗ

| Документ.РеализацияТоваровУслуг КАК Документ

|ГДЕ

| Документ.Дата МЕЖДУ &НачалоПериода И &КонецПериода

| ИЛИ Документ.Дата В (&ТаблицаДат.Период)";

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

Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ТекущаяДата));

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

Обратите внимание, что в этом примере:

  • 📅 Тип колонки Период явно указан как Дата.
  • 🔄 В запросе используется комбинация условий: и диапазон дат (МЕЖДУ), и проверка на вхождение в список (В).
  • 🔍 Параметры НачалоПериода и КонецПериода также типизированы через УстановитьПараметр.
Что делать, если в таблице значений смешанные типы данных?

Если в одной колонке ТЗ могут быть значения разных типов (например, числа и строки), используйте тип"ЛюбаяСсылка" или"Произвольный". Однако в этом случае в запросе придётся явно приводить типы с помощью функций вроде ЗНАЧЕНИЕ или ТИПЗНАЧЕНИЯ. Например:

ГДЕ ЗНАЧЕНИЕ(Документ.Номер) = ЗНАЧЕНИЕ(&Таблица.Номер)

Это замедлит выполнение, поэтому старайтесь избегать смешанных типов в ТЗ.

Сложные случаи: соединение таблиц и агрегация

Таблицы значений часто используются не только для фильтрации, но и для соединения с другими таблицами в запросе. Например, когда нужно получить данные из регистра накопления только для тех номенклатурных позиций, которые есть в ТЗ. Рассмотрим пример с соединением и агрегацией:

// Таблица значений с номенклатурой и планируемым количеством

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

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

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

//.. заполняем таблицу

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

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

"ВЫБРАТЬ

| ТоварыНаСкладах.Номенклатура КАК Номенклатура,

| СУММА(ТоварыНаСкладах.КоличествоОстаток) КАК ФактическийОстаток,

| СУММА(ВТ_План.ПланКоличество) КАК ПланКоличество

|ИЗ

| РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах

| ЛЕВОЕ СОЕДИНЕНИЕ ВТ_План КАК ВТ_План

| ПО ТоварыНаСкладах.Номенклатура = ВТ_План.Номенклатура

|ГДЕ

| ТоварыНаСкладах.Номенклатура В (&ТаблицаПлана.Номенклатура)

|СГРУППИРОВАТЬ ПО

| ТоварыНаСкладах.Номенклатура";

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

// Предварительно создаём временную таблицу ВТ_План из ТаблицаПлана

// (код создания временной таблицы опущен для краткости)

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

В этом примере:

  • 🔗 Используется ЛЕВОЕ СОЕДИНЕНИЕ, чтобы получить все строки из регистра накопления, даже если для них нет данных в ТЗ.
  • 📊 Применяется агрегация (СУММА) для подсчёта фактического остатка и планового количества.
  • 🎯 В условии ГДЕ фильтруем только те номенклатурные позиции, которые есть в ТЗ.

Такой подход позволяет сравнить плановые и фактические данные прямо в запросе, не прибегая к пост-обработке результатов.

💡

При соединении таблиц в запросе с использованием ТЗ всегда проверяйте, какие данные могут отсутствовать. Например, если в ТЗ есть номенклатура, которой нет в регистре накопления, используйте ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ, чтобы не потерять строки.

Оптимизация производительности: что ускоряет, а что тормозит запрос

Работа с таблицами значений в запросах может как ускорить обработку данных, так и значительно её замедлить. Вот ключевые факторы, влияющие на производительность:

⚠️ Внимание: Если таблица значений содержит более 1000 строк, никогда не передавайте её напрямую в запрос через параметр. Вместо этого используйте временные таблицы (ПОМЕСТИТЬ ВТ_..). В противном случае платформа будет преобразовывать каждую строку в отдельный параметр, что приведёт к экспоненциальному росту времени выполнения.

Основные правила оптимизации:

  1. Минимизируйте объём передаваемых данных. Если в ТЗ 10 колонок, а в запросе нужны только 2 — создайте новую ТЗ с нужными колонками.
  2. Используйте индексы. Для временных таблиц можно создавать индексы прямо в запросе:
    ПОМЕСТИТЬ ВТ_Данные
    

    ИНДЕКСИРОВАТЬ ПО Колонка1, Колонка2

  3. Избегайте неявных преобразований типов. Если в ТЗ колонка типа Строка, а в запросе сравнивается с Число, будет принудительно приводить типы, что замедляет выполнение.
  4. Разбивайте сложные запросы. Вместо одного огромного запроса с 10 соединениями лучше сделать 2-3 простых и объединить результаты в коде.

Пример оптимизированного запроса с индексом:

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

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

"ВЫБРАТЬ

| &ТаблицаДанных.Код КАК Код,

| &ТаблицаДанных.Наименование КАК Наименование

|ПОМЕСТИТЬ ВТ_Данные

|ИНДЕКСИРОВАТЬ ПО Код; // Создаём индекс по колонке Код

//----------

ВЫБРАТЬ

| ВТ_Данные.Наименование КАК Наименование,

| Справочник.Номенклатура.Артикул КАК Артикул

|ИЗ

| ВТ_Данные КАК ВТ_Данные

| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Справочник.Номенклатура

| ПО ВТ_Данные.Код = Справочник.Номенклатура.Код";

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

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

В этом примере индекс по колонке Код ускоряет соединение временной таблицы со справочником Номенклатура. Разница в производительности может достигать 10-100 раз на больших объёмах данных.

Типичные ошибки и как их избежать

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

⚠️ Внимание: Если вы используете таблицу значений в запросе, который выполняется на сервере (например, в отчёте с флагом"Выполнять на сервере"), убедитесь, что ТЗ передаётся как параметр запроса, а не как глобальная переменная. В противном случае на сервере возникнет ошибка"Неизвестный идентификатор".

Список типичных ошибок:

  • 🚫 Передача пустой ТЗ в запрос. Если таблица значений пуста, условие ГДЕ Ссылка В (&Таблица) вернёт пустой результат, даже если в базе есть данные. Всегда проверяйте заполненность ТЗ перед выполнением запроса:
    Если Таблица.Количество = 0 Тогда
    

    Возврат Новый ТаблицаЗначений;

    КонецЕсли;

  • 🚫 Несоответствие имён колонок. Если в запросе указано &Таблица.Номенклатура, а в ТЗ колонка называется Товар, возникнет ошибка. Используйте псевдонимы (КАК) для унификации имён.
  • 🚫 Игнорирование транзакций. Если вы создаёте временные таблицы в транзакции, они будут автоматически удалены при откате. Это может привести к ошибкам, если запрос выполняется после ОтменитьТранзакцию.
  • 🚫 Чрезмерное использование В для больших списков. Если в ТЗ 10 000 строк, условие ГДЕ Поле В (&БольшаяТаблица) будет выполняться очень долго. В таких случаях лучше использовать временные таблицы с соединением.

Пример обработки пустой ТЗ:

Если ТаблицаТоваров.Количество = 0 Тогда

Сообщить("Таблица товаров пуста! Запрос не будет выполнен.");

Возврат;

КонецЕсли;

Также стоит помнить, что в некоторых версиях 1С:Предприятие (особенно до 8.3.10) были ограничения на количество параметров в запросе. Если ТЗ содержит более 1000 строк, и вы передаёте её как параметр, запрос может не выполниться. В таких случаях временные таблицы — единственное решение.

Практические примеры: от простого к сложному

Чтобы закрепить материал, рассмотрим несколько практических примеров разной сложности — от базового использования ТЗ до комплексных запросов с агрегацией и соединениями.

Пример 1. Простая фильтрация по списку значений

Задача: получить остатки товаров только для тех позиций, которые есть в таблице значений.

ТаблицаНоменклатуры = Новый ТаблицаЗначений;

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

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

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

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

"ВЫБРАТЬ

| ОстаткиТоваров.Номенклатура КАК Номенклатура,

| ОстаткиТоваров.КоличествоОстаток КАК Остаток

|ИЗ

| РегистрНакопления.ОстаткиТоваров КАК ОстаткиТоваров

|ГДЕ

| ОстаткиТоваров.Номенклатура В (&ТаблицаНоменклатуры.Ссылка)";

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

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

Пример 2. Сравнение план/факт с агрегацией

Задача: получить данные о продажах по регионам и сравнить их с плановыми показателями из ТЗ.

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

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

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

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

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

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

"ВЫБРАТЬ

| Продажи.Регион КАК Регион,

| СУММА(Продажи.СуммаДокумента) КАК ФактическиеПродажи,

| ВТ_План.ПланПродаж КАК ПланПродаж

|ИЗ

| Документ.РеализацияТоваровУслуг КАК Продажи

| ЛЕВОЕ СОЕДИНЕНИЕ ВТ_План КАК ВТ_План

| ПО Продажи.Регион = ВТ_План.Регион

|ГДЕ

| Продажи.Дата МЕЖДУ &НачалоПериода И &КонецПериода

|СГРУППИРОВАТЬ ПО

| Продажи.Регион,

| ВТ_План.ПланПродаж";

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

Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата));

Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата));

// Предварительно создаём ВТ_План из ТаблицаПлана

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

Пример 3. Множественная фильтрация с подзапросами

Задача: получить клиентов, которые покупали товары из ТЗ, но не делали заказов в текущем месяце.

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

//.. заполняем таблицу товарами

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

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

"ВЫБРАТЬ РАЗЛИЧНЫЕ

| Продажи.Клиент КАК Клиент

|ИЗ

| Документ.РеализацияТоваровУслуг КАК Продажи

|ГДЕ

| Продажи.Дата < НАЧАЛОМЕСЯЦА(&ТекущаяДата)

| И Продажи.Номенклатура В (&ТаблицаТоваров.Ссылка)

| И НЕ Продажи.Клиент В (

| ВЫБРАТЬ РАЗЛИЧНЫЕ ПродажиВТекущемМесяце.Клиент

| ИЗ Документ.РеализацияТоваровУслуг КАК ПродажиВТекущемМесяце

| ГДЕ ПродажиВТекущемМесяце.Дата >= НАЧАЛОМЕСЯЦА(&ТекущаяДата)

| )";

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

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

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

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

FAQ: Ответы на частые вопросы

Можно ли использовать таблицу значений в запросе, который выполняется на сервере в фоне?

Да, но с оговорками. Если запрос выполняется на сервере (например, в фоновом задании или отчёте с флагом"Выполнять на сервере"), таблица значений должна передаваться как параметр запроса. Нельзя использовать глобальные переменные или параметры формы — они не доступны на сервере. Также убедитесь, что типы данных колонок ТЗ совместимы с типами полей в запросе.

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

Для больших таблиц значений (от 1000 строк) не рекомендуется передавать их напрямую в запрос через параметр. Вместо этого:

  1. Создайте временную таблицу на сервере с помощью ПОМЕСТИТЬ ВТ_ИмяТаблицы.
  2. Загрузите данные из ТЗ во временную таблицу.
  3. Используйте временную таблицу в основном запросе.

Это ускорит выполнение в десятки раз, так как данные будут обработаны на сервере как обычная таблица БД.

Почему запрос с табли