Работа с таблицами значений в запросах 1С:Предприятие — один из ключевых навыков для разработчиков и администраторов системы. Этот механизм позволяет динамически формировать условия отбора, передавать сложные данные между процедурами и оптимизировать обработку больших массивов информации. Однако не все знают, как правильно интегрировать таблицы значений в SQL-запросы платформы, избегая типичных ошибок и добиваясь максимальной производительности.
В этой статье мы разберём не только базовый синтаксис работы с таблицами значений в запросах, но и продвинутые техники: параметризацию, временные таблицы, оптимизацию для больших объёмов данных. Особое внимание уделим практическим примерам с пояснениями, которые помогут избежать распространённых ошибок при работе с ВЫБРАТЬ, ГДЕ и СОЕДИНИТЬ. Материал будет полезен как начинающим программистам 1С, так и опытным специалистам, которые хотят систематизировать свои знания.
Прежде чем перейти к деталям, важно понять: таблица значений в запросе — это не просто"массив данных", а полноценный инструмент, который может как ускорить выполнение кода, так и значительно его замедлить при неправильном использовании. Далее мы разберём, когда и как его применять.
Что такое таблица значений в контексте запросов 1С
Таблица значений (ТЗ) в 1С:Предприятие — это динамическая структура данных, которая хранит информацию в виде строк и колонок. В отличие от обычных массивов, она позволяет работать с данными как с таблицей базы данных: добавлять колонки, сортировать, фильтровать и даже выполнять SQL-подобные операции. Но как она соотносится с запросами?
В запросах 1С таблица значений может использоваться в трёх основных ролях:
- 📥 Источник данных — когда ТЗ подставляется в запрос как временная таблица (через
ВЫБРАТЬ ИЗ &Таблица). - 🔍 Параметр отбора — когда значения из ТЗ используются в условиях
ГДЕ(например,ГДЕ Ссылка В (&СписокСсылок)). - 🔄 Результат запроса — когда данные из запроса выгружаются в ТЗ для дальнейшей обработки.
Главное отличие от обычных таблиц базы данных — таблица значений существует только в оперативной памяти во время выполнения кода. Это даёт гибкость, но накладывает ограничения: например, нельзя использовать ТЗ в запросах, которые выполняются на сервере 1С без передачи контекста (как в отчётах с серверным выполнением).
Базовый синтаксис: как передать таблицу значений в запрос
Самый простой способ использовать ТЗ в запросе — передать её как параметр с помощью символа &. Рассмотрим минимальный рабочий пример:
// Создаём таблицу значений с данными
ТаблицаТоваров = Новый ТаблицаЗначений;
ТаблицаТоваров.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаТоваров.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
НоваяСтрока = ТаблицаТоваров.Добавить;
НоваяСтрока.Ссылка = Справочники.Номенклатура.НайтиПоНаименованию("Монитор 24\"");
НоваяСтрока.Количество = 5;
// Формируем запрос с использованием ТЗ
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладах.Номенклатура КАК Номенклатура,
| ТоварыНаСкладах.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
|ГДЕ
| ТоварыНаСкладах.Номенклатура В (&ТаблицаТоваров.Ссылка)";
Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаТоваров);
Результат = Запрос.Выполнить;
Обратите внимание на ключевые моменты:
- 🔹 В тексте запроса указываем
&ТаблицаТоваров.Ссылка— это означает, что мы берём колонкуСсылкаиз переданной ТЗ. - 🔹 Метод
УстановитьПараметрсвязывает имя параметра в запросе (&ТаблицаТоваров) с реальной таблицей значений. - 🔹 В условии
ГДЕиспользуется операторВ, который проверяет вхождение значения в список.
Такой подход работает для простых случаев, но имеет ограничение: если в ТЗ много строк, запрос может выполняться медленно из-за преобразования данных. Для оптимизации используют временные таблицы — о них поговорим далее.
Всегда проверяйте типы данных колонок таблицы значений перед передачей в запрос. Например, если в ТЗ колонка имеет тип"Строка", а в запросе сравнивается с полем типа"Число", возникнет ошибка несоответствия типов.
Временные таблицы: ускорение работы с большими данными
Когда таблица значений содержит сотни или тысячи строк, прямой подстановки в запрос через параметр недостаточно — это приводит к длительному парсингу и замедлению выполнения. В таких случаях используют временные таблицы, которые создаются на сервере 1С и существуют только во время сеанса.
Пример использования временной таблицы:
// Создаём таблицу значений с большим объёмом данных
ТаблицаЗаказов = Новый ТаблицаЗначений;
ТаблицаЗаказов.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТаблицаЗаказов.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
//.. заполняем таблицу данными (например, 1000 строк)
// Создаём временную таблицу на сервере
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| &ТаблицаЗаказов.Дата КАК Дата,
| &ТаблицаЗаказов.Сумма КАК Сумма
|ПОМЕСТИТЬ ВТ_Заказы"; // ВТ_ — префикс для временной таблицы
Запрос.УстановитьПараметр("ТаблицаЗаказов", ТаблицаЗаказов);
Запрос.Выполнить;
// Теперь используем временную таблицу в основном запросе
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_Заказы.Дата КАК Дата,
| СУММА(ВТ_Заказы.Сумма) КАК Итого
|ИЗ
| ВТ_Заказы КАК ВТ_Заказы
|СГРУППИРОВАТЬ ПО
| ВТ_Заказы.Дата";
Результат = Запрос.Выполнить;
Преимущества временных таблиц:
- ⚡ Производительность: данные один раз загружаются на сервер и дальше обрабатываются как обычная таблица БД.
- 🔄 Многоразовое использование: одну временную таблицу можно использовать в нескольких запросах.
- 🛠 Гибкость: поддерживаются индексы, соединения (
СОЕДИНИТЬ), агрегатные функции.
Создать таблицу значений с нужной структурой колонок|
Заполнить таблицу данными (не менее 100 строк для заметного эффекта)|
Проверить типы данных колонок на соответствие полям в запросе|
Использовать префикс ВТ_ для имени временной таблицы|
Убедиться, что запрос выполняется на сервере (для клиент-серверного варианта)
-->
Если вам нужно сохранить данные дольше, рассмотрите вариант записи во временное хранилище (ПоместитьВоВременноеХранилище).
Параметризация запросов: как избежать ошибок с типами данных
Одна из самых распространённых ошибок при работе с таблицами значений в запросах — несоответствие типов данных. Например, если в ТЗ колонка имеет тип Строка, а в запросе сравнивается с полем типа Число, платформа 1С выдаст ошибку. Чтобы этого избежать, нужно явно указывать типы при создании ТЗ.
Рассмотрим типичные проблемы и их решения:
| Проблема | Причина | Решение |
|---|---|---|
| Ошибка"Тип не совпадает" | В ТЗ колонка типа Строка, а в запросе сравнивается с Дата |
Использовать ОписаниеТипов("Дата") при добавлении колонки |
| Пустые результаты запроса | В ТЗ передаются NULL-значения, которые игнорируются в условии ГДЕ |
Добавить проверку на ЗНАЧЕНИЕ ЗАПОЛНЕНО в запрос |
| Медленное выполнение | В ТЗ слишком много строк, и они передаются как параметр, а не временная таблица | Использовать конструкцию ПОМЕСТИТЬ ВТ_.. |
Пример корректной параметризации с учётом типов:
ТаблицаДат = Новый ТаблицаЗначений;
ТаблицаДат.Колонки.Добавить("Период",
Новый ОписаниеТипов("Дата", Новый КвалификаторыЧисла(10, 0))); // Явное указание типа
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документ.Дата КАК ДатаДокумента
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Документ
|ГДЕ
| Документ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| ИЛИ Документ.Дата В (&ТаблицаДат.Период)";
Запрос.УстановитьПараметр("ТаблицаДат", ТаблицаДат);
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ТекущаяДата));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(ТекущаяДата));
Обратите внимание, что в этом примере:
- 📅 Тип колонки
Периодявно указан какДата. - 🔄 В запросе используется комбинация условий: и диапазон дат (
МЕЖДУ), и проверка на вхождение в список (В). - 🔍 Параметры
НачалоПериодаиКонецПериодатакже типизированы черезУстановитьПараметр.
Что делать, если в таблице значений смешанные типы данных?
Если в одной колонке ТЗ могут быть значения разных типов (например, числа и строки), используйте тип"ЛюбаяСсылка" или"Произвольный". Однако в этом случае в запросе придётся явно приводить типы с помощью функций вроде ЗНАЧЕНИЕ или ТИПЗНАЧЕНИЯ. Например:
ГДЕ ЗНАЧЕНИЕ(Документ.Номер) = ЗНАЧЕНИЕ(&Таблица.Номер)
Это замедлит выполнение, поэтому старайтесь избегать смешанных типов в ТЗ.
Сложные случаи: соединение таблиц и агрегация
Таблицы значений часто используются не только для фильтрации, но и для соединения с другими таблицами в запросе. Например, когда нужно получить данные из регистра накопления только для тех номенклатурных позиций, которые есть в ТЗ. Рассмотрим пример с соединением и агрегацией:
// Таблица значений с номенклатурой и планируемым количеством
ТаблицаПлана = Новый ТаблицаЗначений;
ТаблицаПлана.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаПлана.Колонки.Добавить("ПланКоличество", Новый ОписаниеТипов("Число"));
//.. заполняем таблицу
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладах.Номенклатура КАК Номенклатура,
| СУММА(ТоварыНаСкладах.КоличествоОстаток) КАК ФактическийОстаток,
| СУММА(ВТ_План.ПланКоличество) КАК ПланКоличество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
| ЛЕВОЕ СОЕДИНЕНИЕ ВТ_План КАК ВТ_План
| ПО ТоварыНаСкладах.Номенклатура = ВТ_План.Номенклатура
|ГДЕ
| ТоварыНаСкладах.Номенклатура В (&ТаблицаПлана.Номенклатура)
|СГРУППИРОВАТЬ ПО
| ТоварыНаСкладах.Номенклатура";
Запрос.УстановитьПараметр("ТаблицаПлана", ТаблицаПлана);
// Предварительно создаём временную таблицу ВТ_План из ТаблицаПлана
// (код создания временной таблицы опущен для краткости)
Результат = Запрос.Выполнить;
В этом примере:
- 🔗 Используется
ЛЕВОЕ СОЕДИНЕНИЕ, чтобы получить все строки из регистра накопления, даже если для них нет данных в ТЗ. - 📊 Применяется агрегация (
СУММА) для подсчёта фактического остатка и планового количества. - 🎯 В условии
ГДЕфильтруем только те номенклатурные позиции, которые есть в ТЗ.
Такой подход позволяет сравнить плановые и фактические данные прямо в запросе, не прибегая к пост-обработке результатов.
При соединении таблиц в запросе с использованием ТЗ всегда проверяйте, какие данные могут отсутствовать. Например, если в ТЗ есть номенклатура, которой нет в регистре накопления, используйте ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ, чтобы не потерять строки.
Оптимизация производительности: что ускоряет, а что тормозит запрос
Работа с таблицами значений в запросах может как ускорить обработку данных, так и значительно её замедлить. Вот ключевые факторы, влияющие на производительность:
⚠️ Внимание: Если таблица значений содержит более 1000 строк, никогда не передавайте её напрямую в запрос через параметр. Вместо этого используйте временные таблицы (ПОМЕСТИТЬ ВТ_..). В противном случае платформа 1С будет преобразовывать каждую строку в отдельный параметр, что приведёт к экспоненциальному росту времени выполнения.
Основные правила оптимизации:
- Минимизируйте объём передаваемых данных. Если в ТЗ 10 колонок, а в запросе нужны только 2 — создайте новую ТЗ с нужными колонками.
- Используйте индексы. Для временных таблиц можно создавать индексы прямо в запросе:
ПОМЕСТИТЬ ВТ_ДанныеИНДЕКСИРОВАТЬ ПО Колонка1, Колонка2
- Избегайте неявных преобразований типов. Если в ТЗ колонка типа
Строка, а в запросе сравнивается сЧисло, 1С будет принудительно приводить типы, что замедляет выполнение. - Разбивайте сложные запросы. Вместо одного огромного запроса с 10 соединениями лучше сделать 2-3 простых и объединить результаты в коде.
Пример оптимизированного запроса с индексом:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| &ТаблицаДанных.Код КАК Код,
| &ТаблицаДанных.Наименование КАК Наименование
|ПОМЕСТИТЬ ВТ_Данные
|ИНДЕКСИРОВАТЬ ПО Код; // Создаём индекс по колонке Код
//----------
ВЫБРАТЬ
| ВТ_Данные.Наименование КАК Наименование,
| Справочник.Номенклатура.Артикул КАК Артикул
|ИЗ
| ВТ_Данные КАК ВТ_Данные
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Справочник.Номенклатура
| ПО ВТ_Данные.Код = Справочник.Номенклатура.Код";
Запрос.УстановитьПараметр("ТаблицаДанных", ТаблицаДанных);
Результат = Запрос.Выполнить;
В этом примере индекс по колонке Код ускоряет соединение временной таблицы со справочником Номенклатура. Разница в производительности может достигать 10-100 раз на больших объёмах данных.
Типичные ошибки и как их избежать
Даже опытные разработчики 1С иногда допускают ошибки при работе с таблицами значений в запросах. Вот наиболее распространённые из них и способы их решения:
⚠️ Внимание: Если вы используете таблицу значений в запросе, который выполняется на сервере (например, в отчёте с флагом"Выполнять на сервере"), убедитесь, что ТЗ передаётся как параметр запроса, а не как глобальная переменная. В противном случае на сервере возникнет ошибка"Неизвестный идентификатор".
Список типичных ошибок:
- 🚫 Передача пустой ТЗ в запрос. Если таблица значений пуста, условие
ГДЕ Ссылка В (&Таблица)вернёт пустой результат, даже если в базе есть данные. Всегда проверяйте заполненность ТЗ перед выполнением запроса:Если Таблица.Количество = 0 ТогдаВозврат Новый ТаблицаЗначений;
КонецЕсли;
- 🚫 Несоответствие имён колонок. Если в запросе указано
&Таблица.Номенклатура, а в ТЗ колонка называетсяТовар, возникнет ошибка. Используйте псевдонимы (КАК) для унификации имён. - 🚫 Игнорирование транзакций. Если вы создаёте временные таблицы в транзакции, они будут автоматически удалены при откате. Это может привести к ошибкам, если запрос выполняется после
ОтменитьТранзакцию. - 🚫 Чрезмерное использование
Вдля больших списков. Если в ТЗ 10 000 строк, условиеГДЕ Поле В (&БольшаяТаблица)будет выполняться очень долго. В таких случаях лучше использовать временные таблицы с соединением.
Пример обработки пустой ТЗ:
Если ТаблицаТоваров.Количество = 0 Тогда
Сообщить("Таблица товаров пуста! Запрос не будет выполнен.");
Возврат;
КонецЕсли;
Также стоит помнить, что в некоторых версиях 1С:Предприятие (особенно до 8.3.10) были ограничения на количество параметров в запросе. Если ТЗ содержит более 1000 строк, и вы передаёте её как параметр, запрос может не выполниться. В таких случаях временные таблицы — единственное решение.
Практические примеры: от простого к сложному
Чтобы закрепить материал, рассмотрим несколько практических примеров разной сложности — от базового использования ТЗ до комплексных запросов с агрегацией и соединениями.
Пример 1. Простая фильтрация по списку значений
Задача: получить остатки товаров только для тех позиций, которые есть в таблице значений.
ТаблицаНоменклатуры = Новый ТаблицаЗначений;
ТаблицаНоменклатуры.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
// Заполняем таблицу нужными товарами
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОстаткиТоваров.Номенклатура КАК Номенклатура,
| ОстаткиТоваров.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ОстаткиТоваров КАК ОстаткиТоваров
|ГДЕ
| ОстаткиТоваров.Номенклатура В (&ТаблицаНоменклатуры.Ссылка)";
Запрос.УстановитьПараметр("ТаблицаНоменклатуры", ТаблицаНоменклатуры);
Результат = Запрос.Выполнить;
Пример 2. Сравнение план/факт с агрегацией
Задача: получить данные о продажах по регионам и сравнить их с плановыми показателями из ТЗ.
ТаблицаПлана = Новый ТаблицаЗначений;
ТаблицаПлана.Колонки.Добавить("Регион", Новый ОписаниеТипов("СправочникСсылка.Регионы"));
ТаблицаПлана.Колонки.Добавить("ПланПродаж", Новый ОписаниеТипов("Число"));
// Заполняем таблицу плановыми данными
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Продажи.Регион КАК Регион,
| СУММА(Продажи.СуммаДокумента) КАК ФактическиеПродажи,
| ВТ_План.ПланПродаж КАК ПланПродаж
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Продажи
| ЛЕВОЕ СОЕДИНЕНИЕ ВТ_План КАК ВТ_План
| ПО Продажи.Регион = ВТ_План.Регион
|ГДЕ
| Продажи.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|СГРУППИРОВАТЬ ПО
| Продажи.Регион,
| ВТ_План.ПланПродаж";
Запрос.УстановитьПараметр("ТаблицаПлана", ТаблицаПлана);
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата));
// Предварительно создаём ВТ_План из ТаблицаПлана
Результат = Запрос.Выполнить;
Пример 3. Множественная фильтрация с подзапросами
Задача: получить клиентов, которые покупали товары из ТЗ, но не делали заказов в текущем месяце.
ТаблицаТоваров = Новый ТаблицаЗначений;
//.. заполняем таблицу товарами
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Продажи.Клиент КАК Клиент
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Продажи
|ГДЕ
| Продажи.Дата < НАЧАЛОМЕСЯЦА(&ТекущаяДата)
| И Продажи.Номенклатура В (&ТаблицаТоваров.Ссылка)
| И НЕ Продажи.Клиент В (
| ВЫБРАТЬ РАЗЛИЧНЫЕ ПродажиВТекущемМесяце.Клиент
| ИЗ Документ.РеализацияТоваровУслуг КАК ПродажиВТекущемМесяце
| ГДЕ ПродажиВТекущемМесяце.Дата >= НАЧАЛОМЕСЯЦА(&ТекущаяДата)
| )";
Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаТоваров);
Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата);
Результат = Запрос.Выполнить;
Эти примеры покрывают большинствоных сценариев использования таблиц значений в запросах 1С. Адаптируйте их под свои задачи, учитывая специфику вашей конфигурации.
FAQ: Ответы на частые вопросы
Можно ли использовать таблицу значений в запросе, который выполняется на сервере в фоне?
Да, но с оговорками. Если запрос выполняется на сервере (например, в фоновом задании или отчёте с флагом"Выполнять на сервере"), таблица значений должна передаваться как параметр запроса. Нельзя использовать глобальные переменные или параметры формы — они не доступны на сервере. Также убедитесь, что типы данных колонок ТЗ совместимы с типами полей в запросе.
Как передать таблицу значений в запрос, если в ней более 10 000 строк?
Для больших таблиц значений (от 1000 строк) не рекомендуется передавать их напрямую в запрос через параметр. Вместо этого:
- Создайте временную таблицу на сервере с помощью
ПОМЕСТИТЬ ВТ_ИмяТаблицы. - Загрузите данные из ТЗ во временную таблицу.
- Используйте временную таблицу в основном запросе.
Это ускорит выполнение в десятки раз, так как данные будут обработаны на сервере как обычная таблица БД.