Почему возникает ошибка несовместимых типов при использовании объекта Граница в запросе 1С

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

При написании отчетов и выборок по документам разработчики часто сталкиваются с ошибкой: «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов». Давайте подробно разберем, почему эта ошибка происходит при попытке передать объект Граница в условие запроса, в каких случаях Граница действительно необходима и как правильно организовывать фильтрацию документов по датам.

Причина возникновения ошибки

Проанализируем проблемную ситуацию. В коде на встроенном языке создаются объекты границ и передаются в параметры запроса:


Граница_1 = Новый Граница(НачалоДня(ПериодОтчета.ДатаНачала), ВидГраницы.Включая);
Граница_2 = Новый Граница(КонецДня(ПериодОтчета.ДатаОкончания), ВидГраницы.Включая);

Запрос.УстановитьПараметр("НачДата", Граница_1);
Запрос.УстановитьПараметр("КонДата", Граница_2);

В тексте запроса используется стандартная проверка попадания в интервал:


ГДЕ
    ЗаказКлиента.Ссылка.Дата МЕЖДУ &НачДата И &КонДата

Выясним причину падения запроса. Объект Граница является специализированным объектом встроенного языка 1С:Предприятия и не соответствует ни одному из базовых типов данных в реляционных СУБД. Поле документа Дата в базе данных хранится как стандартный тип даты и времени (DATETIME).

Когда платформа 1С транслирует условие ГДЕ в SQL-запрос, она пытается сопоставить поле Дата с переданным значением. Поскольку прямое сравнение структуры границы с примитивным типом даты на уровне СУБД невозможно, платформа выдает ошибку о несовместимости типов.

Для чего предназначен объект Граница

Объект Граница разработан исключительно для передачи в параметры виртуальных таблиц регистров (например, остатков или сведений). Генератор запросов платформы 1С знает внутреннюю структуру границы и автоматически преобразует ее в специальный SQL-код при обращении к регистрам.

Рассмотрим классический пример, где использование границы незаменимо: получение остатков перед проведением документа с учетом его позиции на временной оси.


// Получаем остатки строго до момента времени документа (исключая движения самого документа)
ПараметрГраница = Новый Граница(ДокументСсылка.МоментВремени(), ВидГраницы.Исключая);

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Остатки.Номенклатура,
    |   Остатки.КоличествоОстаток
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК Остатки";

Запрос.УстановитьПараметр("Период", ПараметрГраница);

Если бы мы передали просто дату со смещением в минус одну секунду, мы могли бы потерять движения других документов, проведенных в ту же самую секунду, но расположенных до текущего документа.

Варианты правильной фильтрации документов по периоду

Разберем, как корректно отобрать документы за период в реальной практике.

  1. Подготовка дат во встроенном языке перед передачей в запрос

    Самый простой и эффективный способ — привести границы периода к началу и концу дня с помощью стандартных функций НачалоДня() и КонецДня():

    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ЗаказКлиента.Ссылка КАК Ссылка,
        |   ЗаказКлиента.Дата КАК Дата,
        |   ЗаказКлиента.Номер КАК Номер
        |ИЗ
        |   Документ.ЗаказКлиента КАК ЗаказКлиента
        |ГДЕ
        |   ЗаказКлиента.Дата МЕЖДУ &НачДата И &КонДата";
    
    Запрос.УстановитьПараметр("НачДата", НачалоДня(ПериодОтчета.ДатаНачала));
    Запрос.УстановитьПараметр("КонДата", КонецДня(ПериодОтчета.ДатаОкончания));
    
  2. Использование функций языка запросов

    Можно передать произвольные даты и обработать их непосредственно внутри текста запроса функциями НачалоПериода и КонецПериода:

    
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ЗаказКлиента.Ссылка КАК Ссылка,
        |   ЗаказКлиента.Дата КАК Дата
        |ИЗ
        |   Документ.ЗаказКлиента КАК ЗаказКлиента
        |ГДЕ
        |   ЗаказКлиента.Дата МЕЖДУ НачалоПериода(&НачДата, ДЕНЬ) И КонецПериода(&КонДата, ДЕНЬ)";
    
    Запрос.УстановитьПараметр("НачДата", ПериодОтчета.ДатаНачала);
    Запрос.УстановитьПараметр("КонДата", ПериодОтчета.ДатаОкончания);
    

Важные правила производительности и индексации

Обратим внимание на критические нюансы работы с датами в запросах:

  1. Не применяйте функции к полям таблиц: никогда не пишите в условии ГДЕ НачалоПериода(ЗаказКлиента.Дата, ДЕНЬ) = &Дата. Оборачивание поля таблицы в функцию делает невозможным использование индекса по полю Дата, что приводит к полному сканированию всей таблицы базы данных (Table Scan). Всегда применяйте функции только к параметрам.
  2. Сравнение до точной позиции документа: если требуется выбрать документы, созданные строго до определенного документа (включая позицию внутри одной секунды), передать МоментВремени в условие ГДЕ также нельзя. В этом случае используйте составное условие:
    
    ГДЕ
        (ЗаказКлиента.Дата < &ДатаГраницы)
        ИЛИ (ЗаказКлиента.Дата = &ДатаГраницы И ЗаказКлиента.Ссылка <= &СсылкаГраницы)
    
  3. Работа в Системе компоновки данных (СКД): если отчет строится на СКД, используйте тип СтандартныйПериод. В схеме компоновки на вкладке «Параметры» задайте выражения: для &НачалоПериода — &Период.ДатаНачала, а для &КонецПериода — КонецПериода(&Период.ДатаОкончания, "ДЕНЬ").
← На главную