Передача внешней Таблицы Значений (ТЗ) в запрос в качестве параметра — это стандартная и крайне полезная задача в разработке на платформе 1С:Предприятие 8. Однако начинающие разработчики часто сталкиваются с ситуацией, когда запрос возвращает пустой результат, связи между таблицами не отрабатывают, а СУБД выдает непонятные ошибки. В этой статье мы подробно разберем, как правильно передавать ТЗ в запрос, почему нельзя соединять таблицы по строковым наименованиям, как работает функция ВЫРАЗИТЬ и как эффективно отлаживать такие запросы.
Для начала проанализируем базовый механизм работы платформы с внешними источниками данных. Когда мы передаем Таблицу Значений в качестве параметра запроса (например, под именем &ВыборкаЦен), СУБД на уровне базы данных не может напрямую работать с объектом оперативной памяти сервера 1С. Чтобы использовать данные из ТЗ, их необходимо предварительно спроецировать в реляционную структуру.
Для этого в языке запросов 1С предусмотрен специальный механизм — создание временных таблиц. Мы не можем напрямую соединять или фильтровать параметр-ТЗ в основном теле запроса. Сначала мы обязаны поместить его во временную таблицу. Рассмотрим, как выглядит правильное начало пакета запросов:
ВЫБРАТЬ
ВыборкаЦен.Номенклатура КАК Номенклатура,
ВыборкаЦен.Цена КАК Цена,
ВыборкаЦен.Сумма КАК Сумма
ПОМЕСТИТЬ ВТВыборкаЦены
ИЗ
&ВыборкаЦен КАК ВыборкаЦен
;
Только после выполнения этой инструкции создается физическая временная таблица в СУБД (например, в базе данных tempdb для MS SQL Server), к которой в последующих пакетах запроса можно применять левые соединения, группировки и фильтры.
Разберем одну из самых частых и неочевидных ошибок, с которой столкнулся автор темы на форуме. В его запросе использовалась следующая конструкция:
ВЫРАЗИТЬ(ВыборкаЦен.Номенклатура КАК СТРОКА(1024)) КАК Номенклатура
Давайте выясним причину, почему это привело к поломке всего алгоритма получения цен. Многие разработчики ошибочно полагают, что функция ВЫРАЗИТЬ() в языке запросов 1С работает аналогично функциям приведения типов в других языках программирования (например, преобразует число или ссылку в строковое представление). Это не так!
В языке запросов 1С функция ВЫРАЗИТЬ() предназначена строго для двух сценариев:
ОБЪЕДИНИТЬ).Регистратор), и нам нужно обратиться к реквизиту конкретного документа: ВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг).Сделка. Это снижает количество неявных левых соединений на уровне SQL и ускоряет запрос.Если попытаться применить ВЫРАЗИТЬ(Ссылка КАК СТРОКА(1024)), платформа вернет NULL или вызовет критическую ошибку выполнения, поскольку ссылка не является строковым типом данных. В результате соединение по этому полю всегда будет возвращать пустой результат.
Проанализируем вторую серьезную архитектурную проблему в запросе автора: попытку связать временную таблицу товаров из документа и таблицу цен по наименованию номенклатуры:
ПО (ВТТОвары.Номенклатура = ВТВыборкаЦены.Номенклатура)
Соединение таблиц по строковым полям (тем более по наименованиям справочников) в профессиональной разработке 1С считается грубым нарушением стандартов по следующим причинам:
СправочникСсылка. При увеличении объема данных такой запрос начнет критически зависать.Правильное решение: всегда передавать в Таблицу Значений готовые ссылки (тип СправочникСсылка.Номенклатура) и связывать таблицы только по ним.
Когда мы передаем ТЗ в запрос, платформа 1С должна точно знать типы данных каждой колонки, чтобы корректно построить схему временной таблицы в СУБД. Если колонка ТЗ создается «на лету» без явного указания типов, она приобретает составной тип данных или тип Неопределено. В этом случае:
ВыборкаЦен.Номенклатура.Код), так как система не знает, к какому справочнику относится поле.Посмотрим на примере, как правильно программно инициализировать и типизировать Таблицу Значений перед передачей в запрос:
// Создаем описание типов для номенклатуры и числовых показателей
ОписаниеТипаНоменклатура = Новый ОписаниеТипов("СправочникСсылка.Номенклатура");
ОписаниеТипаЧисло = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));
// Инициализируем ТЗ с жесткой типизацией колонок
ТаблицаЦен = Новый ТаблицаЗначений;
ТаблицаЦен.Колонки.Добавить("Номенклатура", ОписаниеТипаНоменклатура);
ТаблицаЦен.Колонки.Добавить("Цена", ОписаниеТипаЧисло);
ТаблицаЦен.Колонки.Добавить("Сумма", ОписаниеТипаЧисло);
// Пример заполнения строки данными
НоваяСтрока = ТаблицаЦен.Добавить();
НоваяСтрока.Номенклатура = ВыбранныйТоварСсылка; // Передаем ссылку, а не текст!
НоваяСтрока.Цена = 1500.00;
НоваяСтрока.Сумма = 1500.00;
Давайте теперь соберем все рекомендации воедино и полностью перепишем исходный запрос автора темы, избавившись от преобразований в строки, исправив левые соединения и сделав код надежным и быстродействующим.
ВЫБРАТЬ
ОтпускТоваровТовары.Ссылка КАК Ссылка,
ОтпускТоваровТовары.НомерСтроки КАК НомерСтроки,
ОтпускТоваровТовары.Номенклатура КАК Номенклатура, // Здесь теперь ссылка, а не наименование!
ОтпускТоваровТовары.СерияНоменклатуры КАК СерияНоменклатуры,
ОтпускТоваровТовары.Партия КАК Партия,
ОтпускТоваровТовары.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
ОтпускТоваровТовары.Количество КАК Количество,
ОтпускТоваровТовары.ИсточникФинансирования КАК ИсточникФинансирования
ПОМЕСТИТЬ ВТТовары
ИЗ
Документ.ОтпускТоваровВОтделение.Товары КАК ОтпускТоваровТовары
ГДЕ
ОтпускТоваровТовары.Ссылка = &Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СебестоимостьТоваров.Регистратор КАК Регистратор,
ЕСТЬNULL(СебестоимостьТоваров.Стоимость, 0) КАК Сумма,
СебестоимостьТоваров.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТРегНакопления
ИЗ
РегистрНакопления.СебестоимостьТоваров КАК СебестоимостьТоваров
ГДЕ
СебестоимостьТоваров.Регистратор = &Регистратор
И СебестоимостьТоваров.ВидДвижения = &ВидДвижения
И СебестоимостьТоваров.Количество = 0
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВыборкаЦен.Номенклатура КАК Номенклатура, // Это типизированная колонка СправочникСсылка.Номенклатура
ВыборкаЦен.Цена КАК Цена,
ВыборкаЦен.Сумма КАК Сумма
ПОМЕСТИТЬ ВТВыборкаЦены
ИЗ
&ВыборкаЦен КАК ВыборкаЦен // Передаем типизированную ТЗ как параметр
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТТовары.Ссылка КАК Ссылка,
ВТТовары.НомерСтроки КАК НомерСтроки,
ВТТовары.Номенклатура КАК Номенклатура,
ВТТовары.СерияНоменклатуры КАК СерияНоменклатуры,
ВТТовары.Партия КАК Партия,
ВТТовары.Количество КАК Количество,
ВТВыборкаЦены.Цена КАК Цена,
ВТРегНакопления.Сумма КАК СуммаРегистра,
ВТВыборкаЦены.Сумма КАК СуммаВыборки
ИЗ
ВТТовары КАК ВТТовары
// Соединяем по ссылкам - это быстро и надежно!
ЛЕВОЕ СОЕДИНЕНИЕ ВТРегНакопления КАК ВТРегНакопления
ПО ВТТовары.Номенклатура = ВТРегНакопления.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ ВТВыборкаЦены КАК ВТВыборкаЦены
ПО ВТТовары.Номенклатура = ВТВыборкаЦены.Номенклатура
Еще одна боль, о которой упоминает автор — это невозможность увидеть данные при отладке в консоли («в консоли параметр не заполнен»). Действительно, стандартные интерактивные консоли запросов 1С не умеют автоматически генерировать и наполнять таблицы значений в параметрах в режиме реального времени. Чтобы обойти это ограничение при тестировании, используйте один из следующих приемов:
&ВыборкаЦен на выборку из реальной таблицы информационной базы (например, виртуальной таблицы РегистрСведений.ЦеныНоменклатуры.СрезПоследних). Убедитесь, что логика связей работает, а затем верните параметр назад в программном коде.Запрос.ВыполнитьПакет(). Вы сможете детально изучить структуру и наполнение каждой временной таблицы прямо в табло отладки Конфигуратора.Подведем краткий итог. Чтобы передача Таблицы Значений в запрос всегда проходила успешно, придерживайтесь трех золотых правил:
ОписаниеТипов.ВЫРАЗИТЬ для перевода ссылок в строки — передавайте в ТЗ только готовые ссылки СправочникСсылка и связывайте таблицы по ним.ПОМЕСТИТЬ, и только потом используйте её в последующих пакетах запроса.