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