К статье
Практический материал

Разработка и Конфигурация · 11 мин

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

Как посчитать количество документов в запросе 1С: функция КОЛИЧЕСТВО, РАЗЛИЧНЫЕ, группировка, подсчёт по периодам и типичные ошибки разработчика.

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

Главный подводный камень — разница между количеством строк таблицы и количеством самих документов. Если выбирать данные из табличной части или регистра, где один документ встречается многократно, простой подсчёт строк даст завышенное число. Поэтому почти всегда нужен вариант КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Ссылка).

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

Базовый запрос: посчитать все документы одного вида

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

ВЫБРАТЬ

КОЛИЧЕСТВО(РеализацияТоваровУслуг.Ссылка) КАК КоличествоДокументов

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

Такой запрос вернёт одну строку с одним числом. В него попадут все документы, включая непроведённые и помеченные на удаление, — это важно учитывать при интерпретации результата.

Чтобы получить только «живые» документы, добавьте отбор. Чаще всего используют условие на проведённость:

ВЫБРАТЬ

КОЛИЧЕСТВО(РеализацияТоваровУслуг.Ссылка) КАК КоличествоДокументов

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

ГДЕ

РеализацияТоваровУслуг.Проведен

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

Подсчёт уникальных документов через РАЗЛИЧНЫЕ

Как только в запросе появляется табличная часть, регистр сведений или соединение с другой таблицей, один документ начинает встречаться в выборке несколько раз. Прямой подсчёт строк в этом случае завышает результат кратно числу строк табличной части.

Правильный приём — ключевое слово РАЗЛИЧНЫЕ внутри агрегатной функции:

ВЫБРАТЬ

КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Товары.Ссылка) КАК КоличествоДокументов

ИЗ

Документ.РеализацияТоваровУслуг.Товары КАК Товары

ГДЕ

Товары.Ссылка.Проведен

Этот запрос обращается к табличной части «Товары», но считает именно документы: каждая уникальная ссылка учитывается один раз, сколько бы строк номенклатуры в документе ни было.

Тот же приём работает при подсчёте документов, сделавших движения по регистру. Например, количество документов, изменявших остатки по складу:

ВЫБРАТЬ

КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Остатки.Регистратор) КАК КоличествоДокументов

ИЗ

РегистрНакопления.ТоварыНаСкладах КАК Остатки

ГДЕ

Остатки.Период МЕЖДУ &НачалоПериода И &КонецПериода

Подсчёт с группировкой: по периодам, контрагентам, организациям

На практике редко нужно одно общее число — обычно требуется разрез: сколько документов создано по месяцам, по каждому контрагенту или складу. Для этого добавляется секция СГРУППИРОВАТЬ ПО.

Пример группировки по месяцам с использованием функции НАЧАЛОПЕРИОДА:

ВЫБРАТЬ

НАЧАЛОПЕРИОДА(Реализация.Дата, МЕСЯЦ) КАК Месяц,

КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Реализация.Ссылка) КАК КоличествоДокументов

ИЗ

Документ.РеализацияТоваровУслуг КАК Реализация

ГДЕ

Реализация.Проведен

И Реализация.Дата МЕЖДУ &НачалоГода И &КонецГода

СГРУППИРОВАТЬ ПО

НАЧАЛОПЕРИОДА(Реализация.Дата, МЕСЯЦ)

УПОРЯДОЧИТЬ ПО

Месяц

Группировка по реквизиту документа строится аналогично — достаточно указать поле в списке выбора и в секции группировки. Например, количество документов по каждому контрагенту:

ВЫБРАТЬ

Реализация.Контрагент КАК Контрагент,

КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Реализация.Ссылка) КАК КоличествоДокументов

ИЗ

Документ.РеализацияТоваровУслуг КАК Реализация

ГДЕ

Реализация.Проведен

СГРУППИРОВАТЬ ПО

Реализация.Контрагент

Итоги по группировкам

Если нужен и разрез, и общая сумма, добавьте секцию ИТОГИ ПО ОБЩИЕ или ИТОГИ ПО с указанием поля. Тогда в результате запроса появятся итоговые строки, которые удобно выводить в отчёт или дерево значений.

Порядок действий
  1. 1
    Определите, что считать: все записи или уникальные документы
  2. 2
    Напишите базовый запрос ВЫБРАТЬ КОЛИЧЕСТВО(...) ИЗ Документ.ВидДокумента
  3. 3
    Добавьте отбор ГДЕ: проведённость, период, организацию
  4. 4
    При необходимости разреза добавьте СГРУППИРОВАТЬ ПО и итоги
  5. 5
    Проверьте результат в консоли запросов на копии базы

Отбор по периоду и другие условия

Ограничение по дате — обязательный элемент почти любого подсчёта: без него запрос сканирует всю таблицу документов, что на больших базах занимает заметное время. Период передаётся через параметры:

ГДЕ

Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода

Учтите, что МЕЖДУ включает границы. Если конец периода задан как начало дня (00:00:00), документы за последний день почти все выпадут из выборки. Надёжнее передавать конец дня — в коде это КонецДня(Дата) — или использовать сравнение Дата < &СледующийДень.

Другие типовые условия: Проведен для исключения черновиков, НЕ ПометкаУдаления, если нужны все существующие документы, отбор по организации, складу или ответственному. Условия комбинируются через И / ИЛИ, при смешивании используйте скобки, чтобы порядок вычисления был однозначным.

Типичные ошибки
01
Подсчёт строк табличной части вместо уникальных документов — результат завышен в несколько раз
02
Конец периода задан началом дня — документы последнего дня не попадают в выборку
03
Отсутствие отбора по проведённости — в число попадают черновики и помеченные на удаление
04
Группировка по полю, которого нет в списке выбора, — запрос падает с ошибкой
05
Подсчёт через обход выборки в цикле вместо агрегатной функции — лишний код и медленная работа

Как получить результат в коде модуля

Запрос с агрегатной функцией возвращает таблицу из одной строки (или нескольких при группировке). В модуле результат обрабатывается стандартно:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Реализация.Ссылка) КАК КоличествоДокументов

|ИЗ

| Документ.РеализацияТоваровУслуг КАК Реализация

|ГДЕ

| Реализация.Проведен

| И Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода";

Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));

Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));

Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда

КоличествоДокументов = Выборка.КоличествоДокументов;

КонецЕсли;

Обратите внимание: даже если документов нет, агрегатный запрос без группировки вернёт одну строку, но значение будет NULL, а не ноль. Поэтому перед использованием результата в арифметике стоит обернуть его проверкой или привести: ?(Выборка.КоличествоДокументов = NULL, 0, ...) либо использовать ЕСТЬNULL прямо в запросе.

bsl
ВЫБРАТЬ ЕСТЬNULL(КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Док.Ссылка), 0) КАК КоличествоДокументов ИЗ Документ.РеализацияТоваровУслуг КАК Док ГДЕ Док.Проведен

Проверка результата и производительность

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

Для проверки корректности группировки полезно временно заменить агрегатную функцию на выборку самих ссылок с РАЗЛИЧНЫЕ — так видно, какие именно документы попали в подсчёт, и легко найти лишние или потерянные.

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

Контрольные точки

Как проверить результат

Число из запроса против списка документов
Норма

Совпадает с количеством строк в списке с тем же отбором

Тревожный признак

Число заметно больше — вероятно, считаются строки табличной части

Поведение на пустом периоде
Норма

Возвращается 0 или NULL, код это обрабатывает

Тревожный признак

Ошибка приведения NULL к числу в модуле

Документы последнего дня периода
Норма

Входят в выборку

Тревожный признак

Не попадают — конец периода задан началом дня

Альтернативы запросу: когда проще без КОЛИЧЕСТВО

Запрос — не единственный способ. Если нужно просто узнать количество документов в журнале интерактивно, достаточно установить отбор в форме списка — платформа покажет число строк. Для разовой аналитики без программирования подойдёт универсальный отчёт или произвольный отчёт на СКД, где группировка и агрегаты настраиваются мышью.

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

Сравнение вариантов

Агрегатный запрос КОЛИЧЕСТВО

Оптимально для кода и отчётов: быстро, один запрос к базе. Требует аккуратности с РАЗЛИЧНЫЕ и NULL

Отбор в форме списка

Подходит для разовой ручной проверки без программирования, но результат нельзя использовать в коде

Отчёт на СКД

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

Цикл по выборке Выбрать()

Просто, но медленно на больших объёмах; оправдано только при попутной обработке документов

Частые вопросы

Чем КОЛИЧЕСТВО() отличается от КОЛИЧЕСТВО(Ссылка)?

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

Почему запрос вернул NULL вместо нуля?

Если ни один документ не удовлетворяет отбору, агрегатная функция возвращает NULL. Оберните её в ЕСТЬNULL(КОЛИЧЕСТВО(...), 0) прямо в тексте запроса — тогда в коде всегда будет число.

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

Используйте ОБЪЕДИНИТЬ ВСЕ: в каждой части выберите ссылку из своей таблицы документа, а затем оберните объединение во внешний запрос с КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Ссылка). ОБЪЕДИНИТЬ ВСЕ нужно, чтобы одинаковые ссылки из разных частей не отсекались досрочно.

Как посчитать документы за текущий месяц без параметров?

Можно вычислить границы прямо в запросе: НАЧАЛОПЕРИОДА(&ТекущаяДата, МЕСЯЦ) и КОНЕЦПЕРИОДА(&ТекущаяДата, МЕСЯЦ), передав один параметр с текущей датой сеанса. Либо передайте обе границы из кода — так нагляднее и проще отлаживать.

Считает ли запрос помеченные на удаление документы?

Да, если не добавить отбор. Пометка удаления не удаляет запись из таблицы, поэтому добавьте условие НЕ ПометкаУдаления или Проведен, если такие документы не должны попадать в подсчёт.

Можно ли посчитать количество в СКД без написания текста запроса?

Да. В схеме компоновки данных достаточно выбрать таблицу документа в наборе данных, а в настройках ресурсов указать поле Ссылка с агрегатной функцией Количество(Различные). СКД сама сформирует нужный текст запроса.

Итог

Что важно запомнить

Для подсчёта документов используйте КОЛИЧЕСТВО с РАЗЛИЧНЫЕ по полю Ссылка, всегда ограничивайте выборку периодом и проведённостью, обрабатывайте NULL на пустом результате и проверяйте цифру в консоли запросов против реального списка документов.