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

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

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

1. Основы: что такое таблица значений и как она связана с запросами

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

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

  • 📌 Источник данных — когда вы передаете таблицу значений в запрос через параметр ИЗ &Таблица.
  • 📌 Приемник результата — когда результат запроса выгружается в новую таблицу значений.
  • 📌 Промежуточный буфер — для временного хранения данных между этапами обработки.

Важно понимать, что при работе с таблицами значений в запросах преобразует их во временные таблицы SQL (если используется СУБД). Это накладывает ограничения: например, нельзя напрямую изменять данные в таблице значений через ОБНОВИТЬ или УДАЛИТЬ — для этого потребуется обход строк в цикле.

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

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

Чтобы использовать таблицу значений в запросе, ее нужно передать как параметр с префиксом &. Рассмотрим базовый шаблон:

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

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

"ВЫБРАТЬ

| ТаблицаСотрудников.ФИО КАК ФИО,

| ТаблицаСотрудников.Должность КАК Должность

|ИЗ

| &ТаблицаСотрудников КАК ТаблицаСотрудников

|ГДЕ

| ТаблицаСотрудников.Стаж > 5";

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

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

Ключевые моменты:

  • 🔹 Имя параметра в запросе (&ТаблицаСотрудников) должно совпадать с именем при установке параметра.
  • 🔹 В теле запроса таблица значений указывается как обычная таблица базы данных, но с псевдонимом (КАК ТаблицаСотрудников).
  • 🔹 Колонки таблицы значений в запросе обращаются по их реальным именам (регистр важен!).

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

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

"ВЫБРАТЬ

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

| ТаблицаЦен.Цена КАК Цена

|ИЗ

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

| ЛЕВОЕ СОЕДИНЕНИЕ &ТаблицаЦен КАК ТаблицаЦен

| ПО Товары.Ссылка = ТаблицаЦен.Товар";

💡

Если в таблице значений есть колонка с типом "Ссылка", в запросе ее можно сравнивать напрямую со ссылками из базы данных. Но для ускорения работы лучше заранее добавить в таблицу значений колонку с УникальныйИдентификатор() ссылки и фильтровать по ней.

3. Типичные ошибки при работе с таблицами значений в запросах

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

Ошибка Причина Как избежать
Запрос возвращает пустой результат Не совпадают имена колонок в таблице значений и запросе (регистр имеет значение!) Проверьте имена колонок через ТаблицаЗначений.Колонки
Ошибка "Неопределенное имя колонки" В таблице значений нет колонки, указанной в запросе Добавьте недостающую колонку или исправьте запрос
Медленное выполнение запроса Таблица значений слишком большая (тысячи строк) и не оптимизирована Используйте ИНДЕКСИРОВАТЬ ПО для часто фильтруемых колонок
Неправильная сортировка Сортировка применяется к таблице значений после выполнения запроса, а не в самом запросе Добавьте УПОРЯДОЧИТЬ ПО в текст запроса

Особенно коварна ошибка с неявным преобразованием типов. Например, если в таблице значений колонка имеет тип "Строка", а в запросе вы сравниваете ее с числом, попытается автоматически преобразовать данные, что может привести к неожиданным результатам. Всегда проверяйте типы колонок:

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

Сообщить(Колонка.Имя + ": " + ТипЗнч(Колонка.Значение));

КонецЦикла;

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

При использовании таблицы значений в запросе создает временную таблицу в SQL, но без индексов. Кроме того, данные передаются между 1С и СУБД, что добавляет накладные расходы. Для больших объемов данных (10 000+ строк) лучше использовать временные таблицы базы данных или оптимизировать структуру таблицы значений (например, оставлять только необходимые колонки).

4. Продвинутые техники: временные таблицы, объединения и подзапросы

Когда стандартных возможностей недостаточно, на помощь приходят продвинутые приемы. Рассмотрим три самых полезных:

4.1. Использование временных таблиц

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

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

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

"ВЫБРАТЬ

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

| СУММА(Количество) КАК ИтогоКоличество

|ПОМЕСТИТЬ ВТ_ИтогиПоТоварам

|ИЗ

| &ТаблицаДвижений КАК ТаблицаДвижений

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

| Товар";

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

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

// Теперь можно работать с временной таблицей ВТ_ИтогиПоТоварам

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

Запрос2.Текст = "ВЫБРАТЬ * ИЗ ВТ_ИтогиПоТоварам";

4.2. Объединение данных из нескольких таблиц значений

Для объединения данных из нескольких источников используйте конструкцию ОБЪЕДИНИТЬ:

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

"ВЫБРАТЬ

| ФИО,

| 'Основной штат' КАК Источник

|ИЗ

| &ОсновнойШтат КАК ОсновнойШтат

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

|ВЫБРАТЬ

| ФИО,

| 'Временный персонал' КАК Источник

|ИЗ

| &ВременныйПерсонал КАК ВременныйПерсонал";

4.3. Подзапросы с таблицами значений

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

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

"ВЫБРАТЬ

| Справочник.Контрагенты.Наименование

|ИЗ

| Справочник.Контрагенты КАК Справочник.Контрагенты

|ГДЕ

| Справочник.Контрагенты.Ссылка В (

| ВЫБРАТЬ

| Контрагент

| ИЗ

| &СписокКонтрагентов КАК СписокКонтрагентов

| )";

Удалить ненужные колонки|Проверить типы данных колонок|Добавить индексы для часто используемых колонок|Оптимизировать количество строк (оставить только релевантные)|Проверить на наличие пустых значений-->

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

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

  • 🚀 Индексирование колонок: Если вы часто фильтруете или сортируете по определенной колонке, добавьте индекс:
    ТаблицаЗначений.Индексы.Добавить("ИндексПоДатам", Новый ИндексТаблицыЗначений("ДатаДокумента"));
  • 🚀 Ограничение данных: Если в таблице значений миллионы строк, но нужен только фрагмент, отфильтруйте данные до передачи в запрос:
    ФильтрованнаяТаблица = ТаблицаЗначений.Скопировать();
    

    ФильтрованнаяТаблица.Очистить();

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

    Если Строка.Дата > НачалоГода(ТекущаяДата()) Тогда

    ФильтрованнаяТаблица.Добавить(Строка);

    КонецЕсли;

    КонецЦикла;

  • 🚀 Использование временных таблиц: Для повторяющихся операций с одной и той же таблицей значений выгрузите ее во временную таблицу базы данных (как показано в разделе 4.1).

Еще один эффективный прием — разбиение больших таблиц. Если таблица значений содержит более 50 000 строк, разбейте ее на части (например, по 10 000 строк) и обрабатывайте каждую часть отдельно. Это снизит нагрузку на память и ускорит выполнение.

💡

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

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

6. Практические примеры: отчеты, обработки и интеграции

Рассмотрим реальные сценарии, где обработка таблиц значений в запросах незаменима.

6.1. Формирование отчета с динамическими параметрами

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

  1. Пользователь выбирает товары в табличном поле формы.
  2. Создается таблица значений с колонками Товар (ссылка) и Наименование (строка).
  3. Таблица передается в запрос для фильтрации данных:
Запрос.Текст =

"ВЫБРАТЬ

| Продажи.Дата,

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

| Продажи.Сумма

|ИЗ

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

|ГДЕ

| Продажи.Товар В (

| ВЫБРАТЬ

| Товар

| ИЗ

| &ВыбранныеТовары КАК ВыбранныеТовары

| )

|УПОРЯДОЧИТЬ ПО

| Дата";

6.2. Сравнение данных из разных источников

Частая задача — сравнить данные из с данными из внешней системы (например, 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. Использование конструктора запросов

Если запрос возвращает неожиданный результат, воспользуйтесь конструктором запросов в конфигураторе:

  1. Откройте окно запроса (Файл → Новый → Запрос).
  2. Вставьте текст вашего запроса.
  3. Нажмите Конструктор запроса и проверьте визуальное представление.
  4. Используйте кнопку Выполнить для тестирования.

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С

Можно ли в таблице значений использовать колонки с типом "Таблица значений"?

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

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

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

ТекстЗапроса = "ВЫБРАТЬ ";

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

ТекстЗапроса = ТекстЗапроса + Колонка.Имя + ", ";

КонецЦикла;

ТекстЗапроса = Лев(ТекстЗапроса, СтрДлина(ТекстЗапроса) - 2) + " ИЗ &Таблица";

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

Причины могут быть следующими:

  1. Отсутствие индексов в таблице значений (в отличие от таблиц базы данных).
  2. Накладные расходы на передачу данных между и СУБД.
  3. Неоптимальная структура таблицы значений (например, избыточные колонки).

Для ускорения попробуйте:

  • Добавить индексы на часто используемые колонки.
  • Уменьшить количество строк и колонок в таблице значений.
  • Использовать временные таблицы базы данных.

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

Нет, в нельзя напрямую выполнять ОБНОВИТЬ или УДАЛИТЬ для таблиц значений в запросах. Для изменения данных нужно:

  1. Получить результат запроса в новую таблицу значений.
  2. Обработать ее в цикле или с помощью методов таблицы значений (НайтиСтроки(), Получить() и т.д.).
  3. Обновить исходную таблицу значений на основе изменений.

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

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

  • Используйте рекурсивные запросы (если СУБД поддерживает).
  • Применяйте методы обхода дерева (ПолучитьСсылкуНаРодителя(), ЭтоГруппа()).
  • Для выгрузки иерархии в таблицу значений используйте циклы с рекурсией.

Пример рекурсивного обхода:

Процедура ОбойтиДерево(Элемент, ТаблицаРезультатов, Уровень = 0)

НоваяСтрока = ТаблицаРезультатов.Добавить();

НоваяСтрока.Элемент = Элемент;

НоваяСтрока.Уровень = Уровень;

Если Элемент.ЭтоГруппа() Тогда

Для Каждого Подчиненный Из Элемент.Подчиненные Цикл

ОбойтиДерево(Подчиненный, ТаблицаРезультатов, Уровень + 1);

КонецЦикла;

КонецЕсли;

КонецПроцедуры