Как в 1С исправить ошибку сравнения строк неограниченной длины и правильно искать документы по дате в запросах?

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

В процессе разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда стандартный запрос к базе данных возвращает ошибку или не находит существующие записи. Типичный пример — поиск документа ПриходныйКассовыйОрдер по дате и текстовому комментарию. Рассмотрим подробнее, почему возникают такие проблемы и как их правильно решать.

Проблема 1: Ошибка сравнения полей неограниченной длины

Разберем ситуацию, когда при выполнении запроса система выдает сообщение: "Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов". Это происходит потому, что поле Комментарий в большинстве конфигураций имеет тип "Строка" с неограниченной длиной. В языке запросов 1С такие поля нельзя напрямую сравнивать с помощью оператора =.

Проанализируем исходный код, вызывающий ошибку:


Запрос.Текст = 
"ВЫБРАТЬ
|    ПриходныйКассовыйОрдер.Ссылка
|ИЗ
|    Документ.ПриходныйКассовыйОрдер КАК ПриходныйКассовыйОрдер
|ГДЕ
|    ПриходныйКассовыйОрдер.Комментарий = &Комментарий";

Для решения этой проблемы мы можем использовать два основных подхода: ограничение длины или использование оператора подобия.

Вариант 1: Использование функции ВЫРАЗИТЬ

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


|ГДЕ
|    ВЫРАЗИТЬ(ПриходныйКассовыйОрдер.Комментарий КАК СТРОКА(1000)) = &Комментарий

Здесь важно помнить, что максимальная длина для функции ВЫРАЗИТЬ составляет 1024 символа. Если реальный комментарий в базе длиннее, часть текста при сравнении будет отсечена, что может привести к неверному результату.

Вариант 2: Использование оператора ПОДОБНО

Оператор ПОДОБНО позволяет сравнивать строки неограниченной длины без их предварительного преобразования. Это зачастую более надежный способ:


|ГДЕ
|    ПриходныйКассовыйОрдер.Комментарий ПОДОБНО &Комментарий

Однако помните, что поиск по строкам неограниченной длины крайне медленный, так как СУБД не может использовать индексы для таких полей. По возможности старайтесь избегать поиска по комментариям в больших таблицах.

Проблема 2: Запрос возвращает пустой результат при поиске по дате

Выясним причину, по которой условие ПриходныйКассовыйОрдер.Дата = &ТекущаяДата часто не находит документы. В 1С тип данных Дата всегда содержит и дату, и время. Если вы передаете в параметр &ТекущаяДата значение со временем (например, "25.05.2023 14:30:15"), а документ был создан в ту же дату, но в другую секунду, то точного совпадения не будет, и запрос вернет 0 записей.

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

Способ 1: Функция НАЧАЛОПЕРИОДА в запросе

Мы можем привести обе даты к началу дня прямо в тексте запроса:


|ГДЕ
|    НАЧАЛОПЕРИОДА(ПриходныйКассовыйОрдер.Дата, ДЕНЬ) = НАЧАЛОПЕРИОДА(&ТекущаяДата, ДЕНЬ)

Хотя этот метод кажется простым, он имеет существенный недостаток. Применение любой функции к полю базы данных (в левой части условия) делает невозможным использование индексов. Это значительно замедляет выполнение запроса на больших объемах данных.

Способ 2: Использование диапазона (Оптимально)

Наиболее правильным с точки зрения производительности является сравнение даты с началом и концом интересующего нас дня. В этом случае поле Дата остается "чистым", и SQL-сервер мгновенно находит нужные записи по индексу.

Посмотрим на пример реализации в коде:


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

Запрос.УстановитьПараметр("НачалоДня", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецДня", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("Комментарий", НужныйТекст);

Дополнительные факторы, влияющие на результат

Если запрос по-прежнему не находит данные, проанализируем следующие моменты:

  1. Чувствительность к регистру: В зависимости от настроек SQL-сервера, поиск "Оплата" и "оплата" может давать разные результаты. Убедитесь, что регистр в параметре совпадает с регистром в базе.
  2. Невидимые символы: В конце комментариев часто остаются пробелы или знаки переноса строки. Попробуйте использовать условие ПОДОБНО &Комментарий + "%", чтобы найти совпадение по началу строки.
  3. Пометка удаления: Если вам нужны только актуальные документы, не забывайте добавлять фильтр: ГДЕ НЕ ПриходныйКассовыйОрдер.ПометкаУдаления.
  4. Часовые пояса: Если база клиент-серверная, функция ТекущаяДата() вернет время сервера. Если у пользователя на компьютере другой часовой пояс, документы могут "попасть" в другой день. Используйте ТекущаяДатаСеанса() для более точного позиционирования.

Оптимизация получения количества записей

Если ваша задача — просто узнать, существуют ли записи, или получить их общее количество, не обязательно выгружать всю выборку и использовать метод Выборка.Количество(). Проанализируем более эффективный способ:


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

Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Если Выборка.Следующий() Тогда
    Колво = Выборка.ОбщееКоличество;
КонецЕсли;

Такой подход заставляет СУБД вернуть только одно число, что гораздо быстрее, чем передавать по сети информацию о тысячах найденных документов.

← На главную