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

Программист 1С v8.3 (Обычные формы)
← На главную

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

Анализ проблемы и причин возникновения ошибок

Рассмотрим стандартную ситуацию: у нас есть объект ТаблицаЗначений, сформированный программно, и мы хотим использовать его в запросе. Типичная ошибка начинающего разработчика — попытка сразу «скрестить» этот параметр с таблицами базы данных в секциях ИЗ или ОБЪЕДИНИТЬ ВСЕ. При этом система выдает сообщение: «Содержимое объекта данных может быть выбрано только во временную таблицу» или «При выборе данных из объекта в запросе не могут присутствовать соединения и объединения».

Причина кроется в архитектуре SQL-запросов 1С. Внешний объект (таблица значений) физически не находится в базе данных. Чтобы сервер СУБД мог выполнить над ним операции соединения, данные нужно сначала «приземлить» — создать временную таблицу в специальном хранилище базы данных и загрузить туда строки из вашего объекта. Только после этого временная таблица становится доступной для полноценных манипуляций.

Шаг 1: Подготовка и типизация таблицы значений

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


// Описываем типы для колонок
МассивТиповЧисло = Новый Массив;
МассивТиповЧисло.Добавить(Тип("Число"));
ОписаниеТиповЧисло = Новый ОписаниеТипов(МассивТиповЧисло, , , Новый КвалификаторыЧисла(15, 2));

ТаблицаСтатуса = Новый ТаблицаЗначений;
ТаблицаСтатуса.Колонки.Добавить("Реализация", Новый ОписаниеТипов("ДокументСсылка.РеализацияТоваровУслуг"));
ТаблицаСтатуса.Колонки.Добавить("Вернул", Новый ОписаниеТипов("Булево"));
ТаблицаСтатуса.Колонки.Добавить("Закрыто", Новый ОписаниеТипов("Булево"));
// Заполнение таблицы данными...

Важный совет: всегда используйте ОписаниеТипов при добавлении колонок, если планируете передавать эту таблицу в запрос. Это гарантирует, что 1С правильно создаст структуру временной таблицы в базе данных.

Шаг 2: Создание первого пакета запроса (Помещение в ВТ)

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТаблицаСтатуса.Реализация КАК Реализация,
    |	ТаблицаСтатуса.Вернул КАК Вернул,
    |	ТаблицаСтатуса.Закрыто КАК Закрыто,
    |	ТаблицаСтатуса.БезПечати КАК БезПечати
    |ПОМЕСТИТЬ ВТ_Статусы
    |ИЗ
    |	&ТаблицаСтатуса КАК ТаблицаСтатуса
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	МЗ.Ссылка КАК Ссылка,
    |	ВТ_Статусы.Вернул КАК Вернул,
    |	ВТ_Статусы.Закрыто КАК Закрыто
    |ИЗ
    |	Документ.МаршрутноеЗадание.ДокументыРеализации КАК МЗ
    |		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Статусы КАК ВТ_Статусы
    |		ПО МЗ.Реализация = ВТ_Статусы.Реализация
    |ГДЕ
    |	МЗ.Ссылка = &СсылкаМЗ";

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

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

Шаг 3: Решение проблем с объединением и типами NULL

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

Выясним причину ошибки «Тип не может быть выбран в запросе > NULL КАК Поле1». Это происходит, когда 1С не может вывести общий тип для колонки. В таких случаях рекомендуется использовать функцию ВЫРАЗИТЬ.


Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВТ_Статусы.Вернул КАК Вернул
    |ПОМЕСТИТЬ ВТ_Подготовленная
    |ИЗ
    |	ВТ_Статусы КАК ВТ_Статусы
    |;
    |
    |ВЫБРАТЬ
    |	МаршрутноеЗадание.Номер КАК Номер,
    |	ВЫРАЗИТЬ(NULL КАК БУЛЕВО) КАК Вернул
    |ИЗ
    |	Документ.МаршрутноеЗадание КАК МаршрутноеЗадание
    |
    |ОБЪЕДИНИТЬ ВСЕ
    |
    |ВЫБРАТЬ
    |	NULL,
    |	ВТ_Подготовленная.Вернул
    |ИЗ
    |	ВТ_Подготовленная КАК ВТ_Подготовленная";

Использование ВЫРАЗИТЬ(NULL КАК ...) позволяет явно указать платформе, какую структуру должна иметь результирующая выборка, что предотвращает падение запроса.

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

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


"ВЫБРАТЬ
|	ТаблицаСтатуса.Реализация КАК Реализация,
|	ТаблицаСтатуса.Вернул КАК Вернул
|ПОМЕСТИТЬ ВТ_Статусы
|ИЗ
|	&ТаблицаСтатуса КАК ТаблицаСтатуса
|ИНДЕКСИРОВАТЬ ПО
|	Реализация"

Это создаст индекс по полю связи в базе данных, что значительно ускорит выполнение ЛЕВОЕ СОЕДИНЕНИЕ во втором пакете запроса.

Менеджер временных таблиц

Рассмотрим ситуацию, когда одну и ту же таблицу значений нужно использовать в нескольких разных объектах Запрос. Вместо того чтобы в каждом запросе дублировать первый пакет с помещением таблицы, мы можем использовать МенеджерВременныхТаблиц.

  1. Создаем объект МенеджерВременныхТаблиц.
  2. Назначаем его объекту Запрос.
  3. Выполняем запрос по помещению таблицы.
  4. Передаем этот же менеджер в другой запрос.

МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос1 = Новый Запрос("ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ");
Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос1.УстановитьПараметр("ТЗ", МояТаблица);
Запрос1.Выполнить();

// Теперь ВТ_Данные живет внутри МенеджерВТ
Запрос2 = Новый Запрос("ВЫБРАТЬ * ИЗ ВТ_Данные");
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
// Запрос2 увидит ВТ_Данные без повторного помещения
Результат = Запрос2.Выполнить();

Итоги и рекомендации

Подведем итоги нашего анализа работы с таблицами значений в запросах:

  1. Обязательная типизация: Всегда задавайте типы колонок в ТаблицаЗначений перед передачей в запрос.
  2. Разделение на пакеты: Помещайте таблицу-параметр во временную таблицу в первом пакете запроса.
  3. Синтаксические ограничения: Не используйте параметр-объект в соединениях (JOIN) или объединениях (UNION) напрямую.
  4. Работа с NULL: При объединении типизируйте пустые поля с помощью функции ВЫРАЗИТЬ.
  5. Производительность: Используйте индексацию по полям связи для больших таблиц.

Соблюдение этих простых правил позволит вам избежать большинства ошибок, связанных с контекстом выполнения запросов, и сделает ваш код более стабильным и производительным.

← На главную