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