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