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