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