Как получить количество строк из запроса 1С и вернуть его из функции?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

Проанализируем исходную ситуацию. Разработчик написал функцию с запросом, который использует агрегатную функцию КОЛИЧЕСТВО() для подсчета строк в табличной части документа. Вот этот код:


Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    ЗаказКлиентаТовары.Ссылка КАК Ссылка,
|    КОЛИЧЕСТВО(ЗаказКлиентаТовары.НомерСтроки) КАК Количество
|ИЗ
|    Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
|ГДЕ
|    ЗаказКлиентаТовары.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
|    ЗаказКлиентаТовары.Ссылка";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Возврат ?????????????

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

Основное решение: Правильная работа с выборкой из результата запроса

Ключевой момент, который нужно понимать: запрос с агрегатными функциями (такими как КОЛИЧЕСТВО(), СУММА()) без группировки или с группировкой по одному значению (как в примере) всегда возвращает одну строку. В этой единственной строке и содержится вычисленный результат.

Объект ВыборкаИзРезультатаЗапроса — это, по сути, курсор, который нужно сначала установить на эту строку, чтобы получить из нее данные. Для этого используется метод Следующий().

Шаг 1: Позиционирование на результате

Перед тем как обращаться к полям выборки, необходимо вызвать метод Выборка.Следующий(). Если результат запроса не пустой, метод вернет Истина и переместит внутренний указатель на первую (и в нашем случае единственную) строку.

Шаг 2: Получение значения

После успешного позиционирования можно обращаться к полям результата по их именам. В нашем запросе мы назвали колонку с количеством строк "Количество" (КАК Количество). Значит, для получения значения нужно использовать конструкцию Выборка.Количество.

Соединим эти шаги в код:


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

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

Этот код уже почти рабочий. Но он содержит скрытую проблему, которая и вызвала у автора ошибку "Операции сравнения на больше-меньше допустимы только для значений совпадающих примитивных типов".

Обработка ошибки сравнения типов и создание надежной функции

Выясним причину ошибки. Что произойдет, если запрос не вернет ни одной строки? Например, если у документа пустая табличная часть "Товары". В этом случае метод Выборка.Следующий() вернет Ложь, условие Если не выполнится, и функция завершит работу без явного оператора Возврат. В 1С это означает, что функция вернет значение Неопределено.

Далее, в вызывающем коде, при попытке сравнить этот результат с числом (например, Если МояФункция() > 10 Тогда ...) платформа справедливо выдаст ошибку, так как нельзя сравнить Неопределено и Число.

Чтобы избежать этого, функция всегда должна возвращать значение ожидаемого типа, в нашем случае — число.

Решение 1: Использование `Иначе`

Самый простой способ — добавить ветку Иначе и в ней возвращать 0.


Если Выборка.Следующий() Тогда
    Возврат Выборка.Количество;
Иначе
    Возврат 0;
КонецЕсли;

Это полностью рабочая и корректная конструкция.

Решение 2: Инициализация переменной (рекомендуемый стиль)

Более универсальный и читаемый подход, который избавляет от лишних вложенных условий и множественных точек выхода из функции, — это инициализация переменной для результата.


Функция ПолучитьКоличествоСтрокТоваров(СсылкаНаДокумент)

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    КОЛИЧЕСТВО(ТЧ.НомерСтроки) КАК Количество
    |ИЗ
    |    Документ.ЗаказКлиента.Товары КАК ТЧ
    |ГДЕ
    |    ТЧ.Ссылка = &Ссылка";

    Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();

    // 1. Инициализируем переменную значением по умолчанию
    КоличествоСтрок = 0; 

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

    // 3. Возвращаем переменную. Она всегда будет иметь тип Число.
    Возврат КоличествоСтрок; 

КонецФункции

Такая структура функции считается хорошей практикой программирования: она имеет одну точку выхода и всегда возвращает предсказуемый результат. Обратите внимание, что мы также немного упростили запрос, убрав из него лишнее поле Ссылка и группировку, так как для получения одного итогового числа они не нужны.

Альтернативные способы получения количества строк

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

1. Выгрузка результата в ТаблицуЗначений

Можно выгрузить результат запроса в таблицу значений и затем получить нужную ячейку. Это удобно, но может быть менее производительно, если результат запроса большой (в нашем случае он всегда состоит из одной строки, так что разница будет невелика).


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

Если РезультатЗапроса.Пустой() Тогда
    Возврат 0;
Иначе
    ТаблицаРезультата = РезультатЗапроса.Выгрузить();
    // Обращаемся к первой строке (индекс 0) и колонке "Количество"
    Возврат ТаблицаРезультата[0].Количество;
КонецЕсли;

2. Получение количества строк без запроса

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

Гораздо эффективнее получить объект документа и обратиться к его табличной части напрямую.


// Этот способ намного быстрее, если нет сложных условий
ОбъектДокумента = СсылкаНаДокумент.ПолучитьОбъект();
КоличествоСтрок = ОбъектДокумента.Товары.Количество();
Возврат КоличествоСтрок;

Этот метод не нагружает СУБД, так как все данные документа считываются в оперативную память одним обращением при вызове ПолучитьОбъект(). Используйте его, если условия задачи это позволяют.

← На главную