В процессе разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда стандартный запрос к базе данных возвращает ошибку или не находит существующие записи. Типичный пример — поиск документа ПриходныйКассовыйОрдер по дате и текстовому комментарию. Рассмотрим подробнее, почему возникают такие проблемы и как их правильно решать.
Разберем ситуацию, когда при выполнении запроса система выдает сообщение: "Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов". Это происходит потому, что поле Комментарий в большинстве конфигураций имеет тип "Строка" с неограниченной длиной. В языке запросов 1С такие поля нельзя напрямую сравнивать с помощью оператора =.
Проанализируем исходный код, вызывающий ошибку:
Запрос.Текст =
"ВЫБРАТЬ
| ПриходныйКассовыйОрдер.Ссылка
|ИЗ
| Документ.ПриходныйКассовыйОрдер КАК ПриходныйКассовыйОрдер
|ГДЕ
| ПриходныйКассовыйОрдер.Комментарий = &Комментарий";
Для решения этой проблемы мы можем использовать два основных подхода: ограничение длины или использование оператора подобия.
Вариант 1: Использование функции ВЫРАЗИТЬ
Мы можем принудительно привести поле неограниченной длины к строке фиксированного размера. Рассмотрим пример:
|ГДЕ
| ВЫРАЗИТЬ(ПриходныйКассовыйОрдер.Комментарий КАК СТРОКА(1000)) = &Комментарий
Здесь важно помнить, что максимальная длина для функции ВЫРАЗИТЬ составляет 1024 символа. Если реальный комментарий в базе длиннее, часть текста при сравнении будет отсечена, что может привести к неверному результату.
Вариант 2: Использование оператора ПОДОБНО
Оператор ПОДОБНО позволяет сравнивать строки неограниченной длины без их предварительного преобразования. Это зачастую более надежный способ:
|ГДЕ
| ПриходныйКассовыйОрдер.Комментарий ПОДОБНО &Комментарий
Однако помните, что поиск по строкам неограниченной длины крайне медленный, так как СУБД не может использовать индексы для таких полей. По возможности старайтесь избегать поиска по комментариям в больших таблицах.
Выясним причину, по которой условие ПриходныйКассовыйОрдер.Дата = &ТекущаяДата часто не находит документы. В 1С тип данных Дата всегда содержит и дату, и время. Если вы передаете в параметр &ТекущаяДата значение со временем (например, "25.05.2023 14:30:15"), а документ был создан в ту же дату, но в другую секунду, то точного совпадения не будет, и запрос вернет 0 записей.
Рассмотрим, как правильно организовать поиск документов за конкретный день.
Способ 1: Функция НАЧАЛОПЕРИОДА в запросе
Мы можем привести обе даты к началу дня прямо в тексте запроса:
|ГДЕ
| НАЧАЛОПЕРИОДА(ПриходныйКассовыйОрдер.Дата, ДЕНЬ) = НАЧАЛОПЕРИОДА(&ТекущаяДата, ДЕНЬ)
Хотя этот метод кажется простым, он имеет существенный недостаток. Применение любой функции к полю базы данных (в левой части условия) делает невозможным использование индексов. Это значительно замедляет выполнение запроса на больших объемах данных.
Способ 2: Использование диапазона (Оптимально)
Наиболее правильным с точки зрения производительности является сравнение даты с началом и концом интересующего нас дня. В этом случае поле Дата остается "чистым", и SQL-сервер мгновенно находит нужные записи по индексу.
Посмотрим на пример реализации в коде:
Запрос.Текст =
"ВЫБРАТЬ
| ПриходныйКассовыйОрдер.Ссылка
|ИЗ
| Документ.ПриходныйКассовыйОрдер КАК ПриходныйКассовыйОрдер
|ГДЕ
| ПриходныйКассовыйОрдер.Дата МЕЖДУ &НачалоДня И &КонецДня
| И ПриходныйКассовыйОрдер.Комментарий ПОДОБНО &Комментарий";
Запрос.УстановитьПараметр("НачалоДня", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецДня", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("Комментарий", НужныйТекст);
Если запрос по-прежнему не находит данные, проанализируем следующие моменты:
ПОДОБНО &Комментарий + "%", чтобы найти совпадение по началу строки.ГДЕ НЕ ПриходныйКассовыйОрдер.ПометкаУдаления.ТекущаяДата() вернет время сервера. Если у пользователя на компьютере другой часовой пояс, документы могут "попасть" в другой день. Используйте ТекущаяДатаСеанса() для более точного позиционирования.Если ваша задача — просто узнать, существуют ли записи, или получить их общее количество, не обязательно выгружать всю выборку и использовать метод Выборка.Количество(). Проанализируем более эффективный способ:
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(*) КАК ОбщееКоличество
|ИЗ
| Документ.ПриходныйКассовыйОрдер КАК ПриходныйКассовыйОрдер
|ГДЕ
| ПриходныйКассовыйОрдер.Дата МЕЖДУ &НачалоДня И &КонецДня";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Если Выборка.Следующий() Тогда
Колво = Выборка.ОбщееКоличество;
КонецЕсли;
Такой подход заставляет СУБД вернуть только одно число, что гораздо быстрее, чем передавать по сети информацию о тысячах найденных документов.