Разработка и Конфигурация · 11 мин
Как посчитать количество в запросе 1С
Как посчитать количество в запросе 1С: агрегатные функции СУММА и КОЛИЧЕСТВО, группировки, итоги, подсчёт уникальных значений и типичные ошибки разработчиков.
Подсчёт количества — одна из самых частых задач при работе с запросами в 1С: нужно узнать число строк в документе, количество записей регистра, число сотрудников в подразделении или суммарное количество товара на складе. Язык запросов 1С предлагает для этого два разных механизма: агрегатную функцию КОЛИЧЕСТВО() и функцию СУММА() по числовому полю. Выбор между ними зависит от того, что именно вы считаете — строки или значения.
Типичная ошибка начинающих разработчиков — путать эти функции. КОЛИЧЕСТВО считает строки (или непустые значения поля), а СУММА складывает числовые значения, например остатки товара в штуках. Если в поле «Количество» документа хранится число упаковок, то «количество» в прикладном смысле считается через СУММА, а через КОЛИЧЕСТВО вы получите лишь число строк табличной части.
Ниже разобраны оба подхода с примерами кода, группировками, подсчётом уникальных значений, итогами и ситуациями, когда запрос возвращает неожиданный результат.
Чем КОЛИЧЕСТВО отличается от СУММА в языке запросов
Агрегатная функция КОЛИЧЕСТВО подсчитывает число значений в выборке. Вариант КОЛИЧЕСТВО(*) считает все строки результата, включая строки с NULL в любых полях. Вариант КОЛИЧЕСТВО(Поле) считает только строки, где указанное поле не равно NULL — это важное отличие, о котором часто забывают.
Функция СУММА работает иначе: она складывает числовые значения поля по всем строкам группы. Если в регистре накопления «ТоварыНаСкладах» есть ресурс «Количество», то суммарный остаток товара в штуках получают именно через СУММА по виртуальной таблице остатков, а не через КОЛИЧЕСТВО.
Простое правило выбора: если нужен ответ на вопрос «сколько записей» — используйте КОЛИЧЕСТВО, если «сколько единиц товара, часов, рублей» — используйте СУММА по соответствующему числовому полю.
Как посчитать количество строк через КОЛИЧЕСТВО(*)
Самая простая задача — узнать, сколько записей содержит таблица или выборка с отбором. Например, посчитать количество проведённых реализаций за период:
ВЫБРАТЬ
КОЛИЧЕСТВО(*) КАК КоличествоДокументов
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И Реализация.Проведен
Запрос вернёт одну строку с одним полем. Если записей нет, результат будет 0, а не NULL — это удобно, потому что не требуется дополнительная проверка на пустое значение при обработке результата в коде.
Если вместо КОЛИЧЕСТВО(*) написать КОЛИЧЕСТВО(Реализация.Комментарий), запрос посчитает только документы, где комментарий заполнен. Такое поведение иногда используют осознанно, например чтобы узнать, сколько контрагентов имеют заполненный ИНН, но случайное использование приводит к заниженным цифрам.
Как посчитать суммарное количество товара через СУММА
Когда речь идёт о количестве в прикладном смысле — штуках, килограммах, часах — правильный инструмент это СУММА. Пример подсчёта остатков товара по всем складам:
ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура,
СУММА(Остатки.КоличествоОстаток) КАК Количество
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК Остатки
СГРУППИРОВАТЬ ПО
Остатки.Номенклатура
Обратите внимание на секцию СГРУППИРОВАТЬ ПО: поле Номенклатура не обёрнуто в агрегатную функцию, поэтому оно обязано входить в группировку. Если забыть группировку, конструктор запросов выдаст ошибку ещё на этапе проверки текста.
Для табличных частей документов логика та же. Чтобы узнать общее количество товара в конкретной реализации, суммируйте поле Количество табличной части «Товары» с отбором по ссылке на документ.
Запрос = Новый Запрос("ВЫБРАТЬ СУММА(Товары.Количество) КАК Количество ИЗ Документ.РеализацияТоваровУслуг.Товары КАК Товары ГДЕ Товары.Ссылка = &Ссылка"); Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент); Выборка = Запрос.Выполнить().Выбрать(); Если Выборка.Следующий() Тогда ИтогКоличество = ?(Выборка.Количество = NULL, 0, Выборка.Количество); КонецЕсли;Как посчитать количество уникальных значений
Частая задача — узнать, сколько разных контрагентов, товаров или складов встречается в выборке. Для этого внутрь функции КОЛИЧЕСТВО добавляют ключевое слово РАЗЛИЧНЫЕ:
ВЫБРАТЬ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Продажи.Контрагент) КАК КоличествоКлиентов
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи
Конструкция считает каждое уникальное значение один раз, даже если контрагент встречается в тысяче строк оборотов. Без РАЗЛИЧНЫЕ вы получили бы общее число строк, что обычно не имеет смысла для такой задачи.
Учтите: КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле) по-прежнему игнорирует NULL. Если поле необязательное и часть строк его не содержит, эти строки в подсчёт не попадут. Когда нужно считать и «пустые» как отдельную категорию, замените NULL на явное значение через ЕСТЬNULL(Поле, ЗНАЧЕНИЕ(...)) до группировки.
Группировки и итоги: два способа разложить количество по измерениям
Есть два способа получить количество в разрезе измерений. Первый — группировка СГРУППИРОВАТЬ ПО: результат содержит только строки групп, по одной на каждую комбинацию измерений. Это оптимально, когда детальные строки не нужны, например для диаграммы или сводного отчёта.
Второй способ — секция ИТОГИ ПО: запрос возвращает и детальные строки, и итоговые записи с подсчитанным количеством по каждой группе. Такой результат удобно обходить с группировкой в коде через Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам) или выводить в табличный документ.
| Критерий | СГРУППИРОВАТЬ ПО | ИТОГИ ПО |
|---|---|---|
| Детальные строки в результате | Нет | Да |
| Объём выборки | Меньше | Больше |
| Типичное применение | Сводные показатели, графики | Отчёты с детализацией, деревья |
| Обход в коде | Линейный | Иерархический по группировкам |
Если нужны только итоги, не тащите детальные строки через ИТОГИ — это лишняя нагрузка на сервер и память. Обратная ошибка — попытка «дособрать» детали к сгруппированному результату вторым запросом, хотя проще было сразу использовать ИТОГИ.
Сравнение вариантов
Считает все строки выборки. Подходит для подсчёта записей, документов, строк табличных частей.
Считает уникальные непустые значения. Подходит для подсчёта разных клиентов, товаров, складов.
Складывает значения. Подходит для остатков в штуках, часов, сумм. При отсутствии строк возвращает NULL, а не 0.
Обработка результата запроса в коде модуля
После выполнения запроса результат нужно корректно прочитать. Для запроса, возвращающего одну строку с количеством, типовой код выглядит так:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ КОЛИЧЕСТВО(*) КАК Количество ИЗ Справочник.Номенклатура";
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
КоличествоЭлементов = Выборка.Количество;
КонецЕсли;
Для СУММА ситуация тоньше: если выборка пуста, агрегат вернёт NULL. Присваивание NULL числовой переменной или дальнейшие арифметические операции с ним приведут к ошибке. Всегда обрабатывайте этот случай через проверку на NULL или функцию ЕСТЬNULL прямо в тексте запроса: ЕСТЬNULL(СУММА(Остатки.КоличествоОстаток), 0) КАК Количество.
Если поле результата названо через КАК, обращайтесь к нему в выборке именно по этому псевдониму. Обращение к несуществующему полю выборки вызывает ошибку исполнения, а не возвращает пустое значение.
- 1Определите, что считаете: строки, уникальные значения или сумму числового поля
- 2Напишите запрос с нужной агрегатной функцией и отбором
- 3Добавьте СГРУППИРОВАТЬ ПО для всех неагрегатных полей
- 4Оберните СУММА в ЕСТЬNULL(..., 0) для защиты от пустого результата
- 5Выполните запрос в консоли запросов и сверьте цифру с данными в базе
- 6Перенесите текст в модуль и обработайте результат через Выбрать() и Следующий()
Типичные ошибки и как их проверить
Первая частая ошибка — смешение агрегатных и обычных полей без группировки. Запрос ВЫБРАТЬ Номенклатура, КОЛИЧЕСТВО(*) ... без СГРУППИРОВАТЬ ПО Номенклатура не выполнится. Конструктор запросов подсвечивает такую проблему сразу, поэтому пишите сложные запросы именно через конструктор.
Вторая ошибка — подсчёт по таблице с соединением, которое размножает строки. Если к документам присоединить табличную часть, один документ даст столько строк, сколько позиций в нём, и КОЛИЧЕСТВО(*) завысит результат. Решение — считать КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Ссылка) или выполнять подсчёт до соединения во временной таблице.
Третья ошибка — использование периодичности виртуальных таблиц без необходимости. Для подсчёта итогового количества по остаткам берите виртуальную таблицу Остатки, а не Обороты с последующим суммированием, иначе легко получить неверный знак или лишние движения.
Как проверить результат
Совпадает с числом строк, которое видно в списке при тех же отборах
Значение кратно больше — вероятно лишнее соединение
Возвращается 0 благодаря ЕСТЬNULL
Возвращается NULL и ломает расчёты в коде
Сумма групповых значений равна общему итогу без группировки
Итоги не сходятся — часть строк потеряна условием или соединением
FAQ: частые вопросы о подсчёте количества в запросах 1С
Чем КОЛИЧЕСТВО() отличается от КОЛИЧЕСТВО(Ссылка)?
КОЛИЧЕСТВО() считает все строки выборки без исключений. КОЛИЧЕСТВО(Ссылка) считает строки, где поле Ссылка не NULL. Для таблиц документов и справочников ссылка заполнена всегда, поэтому результат совпадёт, а вот для левых соединений, где ссылка может быть NULL, результаты разойдутся.
Как посчитать количество строк табличной части документа?
Обратитесь к табличной части как к отдельной таблице: ВЫБРАТЬ КОЛИЧЕСТВО(*) ИЗ Документ.РеализацияТоваровУслуг.Товары ГДЕ Ссылка = &Ссылка. Запрос вернёт число позиций в документе. Для суммарного количества в штуках используйте СУММА по полю Количество.
Почему СУММА вернула NULL вместо нуля?
Агрегатные функции, кроме КОЛИЧЕСТВО, возвращают NULL, когда в выборке нет ни одной строки. Это стандартное поведение. Оберните выражение в ЕСТЬNULL(СУММА(Поле), 0) или проверьте значение на NULL в коде перед использованием.
Как посчитать количество по нескольким группам сразу, например по складам и номенклатуре?
Перечислите оба поля в секции СГРУППИРОВАТЬ ПО через запятую: СГРУППИРОВАТЬ ПО Склад, Номенклатура. Запрос вернёт по строке на каждую комбинацию склада и товара. Если нужны ещё и промежуточные итоги только по складам, используйте ИТОГИ ПО Склад вместо группировки.
Что быстрее: КОЛИЧЕСТВО() или обход результата запроса со счётчиком?
КОЛИЧЕСТВО() быстрее и экономичнее: подсчёт выполняется на стороне СУБД, а в прикладной код передаётся одно число. Обход выборки со счётчиком заставляет перекачивать все строки на сервер 1С, что при больших объёмах заметно медленнее и расходует память.
Можно ли использовать КОЛИЧЕСТВО в секции ГДЕ?
Нет, агрегатные функции нельзя помещать в условие ГДЕ, потому что отбор применяется до группировки. Для фильтрации уже сгруппированных данных служит секция ИМЕЮЩИЕ: например, ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 5 оставит только группы, где больше пяти строк.
Что важно запомнить
Строки считают через КОЛИЧЕСТВО(*), уникальные значения — через КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле), числовые остатки и количества — через СУММА с обязательной защитой ЕСТЬNULL. Неагрегатные поля требуют СГРУППИРОВАТЬ ПО, а соединения перед подсчётом проверяйте на размножение строк.