При работе с запросами в системе 1С:Предприятие 8 начинающие разработчики часто сталкиваются с ситуацией, когда визуально правильный запрос не возвращает данных. Особенно остро это проявляется при попытке наложить фильтр по полям с типом Дата, СправочникСсылка или при использовании строковых ресурсов. В данной статье мы подробно разберем, почему нельзя склеивать текст запроса вручную, как работать с типами данных и как правильно обрабатывать результаты выборки.
Рассмотрим типичную ошибку, с которой столкнулся автор темы. Попытка вставить значение переменной напрямую в текст запроса через оператор "+" (конкатенацию) — это путь к неработающему коду и уязвимостям. Посмотрим на этот пример:
Текст = "... ГДЕ Ресурс1 = """ + ДатаДокумента + """ ";
Почему это не работает:
'ГГГГММДДЧЧММСС' или через функцию ДАТАВРЕМЯ().Правильное решение: Всегда используйте параметры запроса, обозначаемые символом &. Проанализируем верный подход:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДвиженияУФДляСписания.Период КАК ДатаУФ
|ИЗ
| РегистрСведений.ДвиженияУФДляСписания КАК ДвиженияУФДляСписания
|ГДЕ
| ДвиженияУФДляСписания.Ресурс1 = &ДатаДокумента";
Запрос.УстановитьПараметр("ДатаДокумента", Объект.Дата);
РезультатЗапроса = Запрос.Выполнить();
Использование метода УстановитьПараметр гарантирует, что платформа сама передаст значение в нужном для базы данных формате, сохранив тип Дата.
Часто в регистрах сведений (РС) по ошибке или неопытности назначают ресурсам тип Строка, хотя там должны храниться ссылки на объекты (например, на справочник Организации). Рассмотрим ситуацию, когда в регистре записано наименование организации строкой, а в запросе мы пытаемся сравнить его со ссылкой из документа.
Важный нюанс: Ссылка — это уникальный идентификатор (GUID), а строка — это просто набор символов. Они никогда не будут равны. Если Ресурс3 в вашем регистре имеет тип Строка, то передача в параметр Объект.Организация (которая является ссылкой) приведет к пустому результату запроса.
Выясним, как поступить, если тип данных в регистре изменить нельзя:
Запрос.УстановитьПараметр("Организация", Объект.Организация.Наименование);.СправочникСсылка.Организации. Это обеспечит ссылочную целостность (если организацию переименуют, связь не разорвется).Посмотрим, как выглядит ошибка "двойного поиска", упомянутая в обсуждении:
Запрос.УстановитьПараметр("ВыбРесурс3", Справочники.Организации.НайтиПоНаименованию(Организация));
Если переменная Организация уже содержит ссылку (например, получена из Объект.Организация), то повторный поиск по наименованию бессмыслен. Вы просто заставляете систему выполнять лишнюю работу, которая к тому же может вернуть не тот элемент, если в базе есть дубликаты с одинаковыми именами.
После выполнения запроса мы получаем объект РезультатЗапроса. Чтобы прочитать данные, необходимо создать выборку. Проанализируем, как правильно работать с курсором выборки.
Многие новички забывают вызвать метод Следующий(), без которого данные недоступны. Рассмотрим два сценария:
Сценарий 1: Если ожидается несколько записей
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Здесь мы обрабатываем каждую строку
Сообщить("Найдена дата: " + Выборка.ДатаУФ);
КонецЦикла;
Сценарий 2: Если нужно проверить только наличие записи или получить одно значение
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
// Запись найдена, можно обратиться к полям
Объект.СнятьСЗаписи = Истина;
Иначе
// Записей не найдено
КонецЕсли;
Важное замечание по коду из темы: Автор пытался присваивать значения переменным внутри цикла Пока Выборка.Следующий(), но при этом условия проверки выносил за пределы цикла. В таком случае в переменных останутся данные только от последней записи выборки. Если ваша задача — проверить наличие конкретной записи, лучше перенести логику внутрь цикла или использовать метод РезультатЗапроса.Пустой().
Если ваша цель — просто узнать, есть ли в регистре запись с такими параметрами, не нужно выбирать все поля и крутить цикл. Достаточно проверить результат на пустоту. Разберем наиболее производительный вариант кода:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| 1
|ИЗ
| РегистрСведений.ДвиженияУФДляСписания КАК ДвиженияУФДляСписания
|ГДЕ
| ДвиженияУФДляСписания.Ресурс1 = &Дата
| И ДвиженияУФДляСписания.Ресурс2 = &Номер
| И ДвиженияУФДляСписания.Ресурс3 = &Организация";
Запрос.УстановитьПараметр("Дата", Объект.Дата);
Запрос.УстановитьПараметр("Номер", Объект.Номер);
Запрос.УстановитьПараметр("Организация", Объект.Организация);
Если НЕ Запрос.Выполнить().Пустой() Тогда
Объект.ДокументГотовДляРедактирования = Истина;
КонецЕсли;
Почему этот код лучше:
ВЫБРАТЬ ПЕРВЫЕ 1 ускоряет работу СУБД, так как поиск прекращается сразу после нахождения первого совпадения.Пустой() работает быстрее, чем создание объекта выборки и вызов Следующий().Объект, исключая лишние промежуточные переменные и ошибки преобразования типов.Для стабильной работы ваших выборок всегда придерживайтесь следующих правил:
Запрос.Текст. Только параметры через УстановитьПараметр().Следующий() перед обращением к данным выборки.Пустой() объекта РезультатЗапроса.