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

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

При работе с запросами в 1С начинающие разработчики часто сталкиваются с ситуацией, когда запрос не возвращает ожидаемые данные или работает некорректно, если в условии ГДЕ используется поле с составным типом данных. Ситуация осложняется тем, что система не выдает ошибку, а просто возвращает пустой результат. В этой статье мы подробно разберем, как правильно работать с такими полями, оптимизировать код и на что обратить внимание при передаче параметров.

Анализ исходной проблемы

Рассмотрим ситуацию: у нас есть документ СдельныйНаряд, в котором поле Исполнитель имеет составной тип (например, Справочник.ФизическиеЛица и Справочник.Бригады). Программист пытается выполнить следующий запрос:


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

Запрос.УстановитьПараметр("Исполнитель", Объект.Исполнитель);
Запрос.УстановитьПараметр("НачДата", Объект.Начало);
Запрос.УстановитьПараметр("КонДата", Объект.Конец);

РезультатЗапроса = Запрос.Выполнить();

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

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

Одной из главных причин "молчаливого" отказа запроса или его медленной работы является природа составных типов в базе данных SQL. Когда поле составное, платформа 1С хранит его в нескольких колонках (идентификатор типа и сама ссылка). При обычном сравнении Исполнитель = &Исполнитель СУБД приходится выполнять сложные соединения (LEFT JOIN) со всеми таблицами, входящими в составной тип.

Для оптимизации и корректной работы мы рекомендуем использовать оператор ВЫРАЗИТЬ. Он явно указывает системе, какой тип данных мы ожидаем в этом поле. Проанализируем, как изменится условие:

Вариант с явным приведением типа:


|ГДЕ
|    ВЫРАЗИТЬ(СдельныйНаряд.Исполнитель КАК Справочник.ФизическиеЛица) = &Исполнитель

Преимущества этого подхода:

  1. Производительность: SQL-оптимизатор понимает, к какой конкретной таблице нужно обращаться, и может эффективно использовать индексы.
  2. Надежность: Мы исключаем попадание в выборку данных других типов, которые могли бы вызвать неоднозначность.

Решение 2: Проверка типа через оператор ССЫЛКА

Если в параметр &Исполнитель может быть передано как физическое лицо, так и бригада, использование только ВЫРАЗИТЬ может привести к тому, что значения другого типа будут отсечены (превращены в NULL). В таких случаях правильно будет сначала проверить тип данных:


|ГДЕ
|    (СдельныйНаряд.Исполнитель ССЫЛКА Справочник.ФизическиеЛица И СдельныйНаряд.Исполнитель = &Исполнитель)
|    ИЛИ (СдельныйНаряд.Исполнитель ССЫЛКА Справочник.Бригады И СдельныйНаряд.Исполнитель = &Исполнитель)

Этот подход гарантирует, что запрос отработает корректно для любого из типов, входящих в составной реквизит.

Решение 3: Проблема границ периода (Дата и Время)

Часто проблема "неработающего" запроса кроется не в составном типе, а в параметре &КонДата. Выясним причину: если пользователь выбирает дату в поле Объект.Конец (например, 31.10.2023), то по умолчанию время устанавливается в 00:00:00. Если документ был создан 31.10.2023 в 12:00:00, он не попадет в интервал МЕЖДУ, так как он позже начала дня.

Как правильно передавать дату:


// Обязательно используйте КонецДня для верхней границы периода
Запрос.УстановитьПараметр("КонДата", КонецДня(Объект.Конец));

Либо используйте объект Граница, чтобы включить документы, записанные в ту же секунду:


Запрос.УстановитьПараметр("КонДата", Новый Граница(КонецДня(Объект.Конец), ВидГраницы.Включая));

Решение 4: Различие между Неопределено и Пустой ссылкой

Проанализируем еще одну тонкость составных типов. Поле может содержать значение Неопределено (если значение вообще не выбрано) или ПустаяСсылка конкретного типа (например, пустая ссылка справочника ФизическиеЛица).

Если в параметре &Исполнитель передается Неопределено, а в базе данных записана пустая ссылка конкретного типа, то условие СдельныйНаряд.Исполнитель = &Исполнитель вернет ЛОЖЬ. Перед выполнением запроса убедитесь, что в параметр передается именно то значение, которое ожидается в базе. Для проверки заполнения в коде можно использовать ЗначениеЗаполнено(Объект.Исполнитель).

Важные рекомендации по индексации

Для того чтобы фильтрация по составному полю в больших таблицах не вызывала "тормозов", необходимо убедиться, что для этого реквизита в метаданных установлено свойство Индексировать или Индексировать с дополнительным упорядочиванием.

Однако помните, что индексация составного поля всегда менее эффективна, чем индексация простого ссылочного поля. Поэтому везде, где логика приложения позволяет, старайтесь использовать оператор ВЫРАЗИТЬ в тексте запроса. Это считается "золотым стандартом" разработки на платформе 1С:Предприятие 8.

Итоговый вариант "правильного" кода

С учетом всех разобранных нюансов, наиболее надежный и быстрый вариант кода будет выглядеть так:


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

// Правильная установка параметров
Запрос.УстановитьПараметр("Исполнитель", Объект.Исполнитель);
Запрос.УстановитьПараметр("НачДата", НачалоДня(Объект.Начало));
Запрос.УстановитьПараметр("КонДата", КонецДня(Объект.Конец));

РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
    Сообщить("Данные не найдены. Проверьте заполнение исполнителя и период!");
КонецЕсли;

Разбирая подобные ситуации, мы видим, что мелочи вроде времени в дате или особенностей хранения составных типов в SQL могут существенно влиять на результат работы программы. Внимательное отношение к типам данных и границам периодов — залог стабильной работы ваших разработок.

← На главную