При работе с языком запросов 1С программисты часто сталкиваются с ситуацией, когда в результате выполнения запроса одно из полей содержит не просто значение (строку, число или ссылку), а целую таблицу. В консоли запросов или в отладчике это выглядит как «Таблица значений» или ВыборкаИзРезультатаЗапроса — в анализе поможет инструмент оптимизации запросов и кода 1С. Попытки просто прочитать это поле как обычную переменную приводят к ошибкам или получению «пустых» данных.
В этой статье мы подробно разберем, почему возникают такие «таблицы в ячейках», как правильно извлечь из них данные программно, как увидеть их содержимое в процессе отладки и какими методами лучше заменить такие конструкции для повышения производительности системы.
Прежде чем переходить к решению, нам необходимо четко разграничить два понятия, которые часто путают начинающие разработчики. От этого зависит понимание того, что именно мы пытаемся «вытащить».
ИЗ или ГДЕ. Он выступает как временный источник данных для основного запроса. Результат такого запроса не виден в ячейках итоговой выборки — он просто помогает сформировать общую таблицу.ВЫБРАТЬ. Именно он создает структуру, при которой в каждой строке основного результата лежит еще одна маленькая таблица.Рассмотрим пример вложенной выборки, которая и вызывает описываемую проблему:
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
(ВЫБРАТЬ
Товары.Номенклатура,
Товары.Количество
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Ссылка = Реализация.Ссылка) КАК СоставТоваров
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
В данном случае поле СоставТоваров будет содержать объект типа ВыборкаИзРезультатаЗапроса. Рассмотрим, как с этим работать.
Если мы уже получили результат запроса, содержащий вложенную выборку, мы не можем обратиться к ней как к ТаблицеЗначений напрямую. Платформа 1С возвращает иерархическую структуру, которую нужно обходить последовательно. Проанализируем правильный алгоритм действий.
Сначала мы вызываем метод Выбрать() у основного результата запроса, а затем внутри цикла вызываем Выбрать() у поля, содержащего вложенную таблицу. Посмотрим на пример кода:
Результат = Запрос.Выполнить();
ВыборкаДокументов = Результат.Выбрать();
Пока ВыборкаДокументов.Следующий() Цикл
// Обрабатываем данные основного запроса
Сообщить("Документ: " + ВыборкаДокументов.Документ);
// Получаем вложенную выборку из псевдонима "СоставТоваров"
ВыборкаТовары = ВыборкаДокументов.СоставТоваров.Выбрать();
// Вложенный цикл для обхода "таблицы в ячейке"
Пока ВыборкаТовары.Следующий() Цикл
// Теперь мы имеем доступ к полям вложенного запроса
Сообщить("--- Номенклатура: " + ВыборкаТовары.Номенклатура);
Сообщить("--- Количество: " + ВыборкаТовары.Количество);
КонецЦикла;
КонецЦикла;
Важный момент: Поле СоставТоваров в данном контексте ведет себя как объект РезультатЗапроса, у которого есть свой метод Выбрать(). Именно это позволяет нам входить на уровень глубже.
Часто разработчики жалуются, что при попытке посмотреть значение такого поля в отладчике (через F2 или в окне вычисления выражений) они видят техническое описание объекта, но не сами данные. Чтобы визуализировать «таблицу внутри поля», выполним следующие шаги:
Следующий()).ВыборкаДокументов.СоставТоваров.Выгрузить() (поможет инструмент для пошаговой отладки кода 1С).Метод Выгрузить() принудительно преобразует объект ВыборкаИзРезультатаЗапроса в привычную Таблицу Значений. Только в этом случае вы сможете увидеть колонки и строки в удобном табличном виде. Без вызова Выгрузить() отладчик показывает лишь оболочку итератора.
Использование подзапросов в секции ВЫБРАТЬ (вложенных выборок) считается плохой практикой с точки зрения производительности, особенно если данных много. СУБД вынуждена выполнять отдельный микро-запрос для каждой строки основного результата. Разберем более правильный архитектурный подход — использование временных таблиц.
Проанализируем ситуацию: вместо того чтобы «вкладывать» таблицу товаров в каждую строку документов, мы можем подготовить данные заранее. Рассмотрим шаги:
// Шаг 1: Создаем временную таблицу товаров
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ.Ссылка КАК ДокументСсылка,
| ТЧ.Номенклатура,
| ТЧ.Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
|ИНДЕКСИРОВАТЬ ПО
| ДокументСсылка;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Реализация.Ссылка КАК Документ,
| ВТ_Товары.Номенклатура,
| ВТ_Товары.Количество
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
| ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Товары КАК ВТ_Товары
| ПО Реализация.Ссылка = ВТ_Товары.ДокументСсылка";
Результат = Запрос.Выполнить();
Этот метод работает значительно быстрее. Однако стоит учитывать, что в этом случае строки основного документа будут дублироваться для каждой строки товара. Если вам нужно именно дерево (сгруппированные данные), вы можете использовать метод Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам).
Разберем типичную ситуацию: программист пытается сделать ЛЕВОЕ СОЕДИНЕНИЕ внутри вложенного запроса, который сам находится в секции ВЫБРАТЬ. Это часто приводит к ошибкам компиляции запроса. Выясним причину:
Во вложенной выборке (внутри скобок в блоке ВЫБРАТЬ) нельзя обращаться к таблицам, которые не определены внутри этих же скобок, если только вы не используете корреляционную связь через ГДЕ. Платформа 1С строго ограничивает область видимости таблиц. Если вам нужно сложное соединение нескольких таблиц, чтобы получить «вложенный результат», лучше отказаться от вложенных выборок в пользу обычного плоского запроса с последующей группировкой данных в ТаблицеЗначений или ДеревеЗначений на стороне встроенного языка.
Выяснив причину появления «Таблицы значений» в поле запроса, мы понимаем, что это удобный, но ресурсоемкий инструмент. Для работы с ним:
.Выгрузить().Помните, что чистота и производительность кода напрямую зависят от того, насколько эффективно используются ресурсы сервера СУБД, а вложенные выборки в списке полей — это первый кандидат на оптимизацию.