Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда написанный запрос упорно отказывается выводить итоги. Вроде бы и секция ИТОГИ написана, и поля выбраны правильно, но на выходе мы получаем плоскую таблицу без намека на иерархию или промежуточные суммы. Особенно часто эта проблема возникает при переносе кода из старых отчетов, написанных на построителе, или при попытке сгруппировать сложные текстовые поля.
В этой статье мы подробно разберем по шагам, почему в запросах могут не считаться итоги, проанализируем неочевидные платформенные ограничения и выясним, как правильно составить запрос, чтобы получить корректный результат.
Давайте проанализируем исходный запрос, представленный в начале темы. В нем можно заметить конструкции следующего вида:
{УПОРЯДОЧИТЬ ПО Док.*, Дата, Номер...}
{ИТОГИ ПО Док.*}
Фигурные скобки {} в языке запросов 1С имеют строго определенное назначение. Они указывают платформе, что данные поля и секции являются необязательными и предназначены для автоматического заполнения механизмами Построителя отчетов или Системы компоновки данных (СКД).
Когда вы выполняете такой запрос напрямую через консоль запросов или в коде с помощью метода Запрос.Выполнить(), платформа полностью игнорирует все, что находится внутри фигурных скобок. В результате секция ИТОГИ просто отбрасывается СУБД, и никаких расчетов не происходит.
Решение: Если вы пишете обычный запрос, который планируете обходить в коде программно (например, через выборку с обходом по группировкам), вам необходимо убрать фигурные скобки и объявить итоги явно:
// Неправильно (для прямого выполнения):
{ИТОГИ ПО Док}
// Правильно:
ИТОГИ ПО Док
Обратите внимание, что для вывода итогов в консоли запросов результат необходимо выводить в виде Дерева, а не плоского списка, иначе иерархия итогов также не будет видна визуально.
Еще одна грубая синтаксическая ошибка в исходном коде — размещение секции итогов внутри пакета запроса, который помещает данные во временную таблицу:
ВЫБРАТЬ
...
ПОМЕСТИТЬ ВТПоступления
ИЗ
Документ.ПоступлениеТоваровУслуг КАК ПоступлениеТоваровУслуг
...
{ИТОГИ ПО Док.*} ;
Если мы уберем фигурные скобки и попытаемся выполнить этот пакет, платформа выдаст ошибку: «Не допускается использование итогов в запросе, результаты которого помещаются во временную таблицу».
Выясним причину этого ограничения. Временная таблица — это физическая таблица, которая создается в базе данных на стороне СУБД (например, в базе tempdb на MS SQL Server). По своей структуре она обязана быть плоской реляционной таблицей. Секция ИТОГИ превращает результат запроса в иерархическую структуру (дерево), содержащую как детальные записи, так и строки итогов по группировкам. Записать такую структуру в плоскую таблицу СУБД невозможно.
Решение: Все промежуточные расчеты и помещения во временные таблицы делайте без использования итогов. Секцию ИТОГИ необходимо применять исключительно в самом последнем, финальном запросе пакета, который возвращает результат в вызывающую программу.
При расчете итогов недостаточно просто указать, в разрезе каких полей мы хотим сгруппировать данные. Мы обязаны явно перечислить, какие именно показатели мы хотим рассчитать и по какому алгоритму.
Если вы напишете просто:
ИТОГИ ПО Док
то платформа не поймет, что делать с остальными полями выборки (количеством, суммами, реквизитами шапки). Нам нужно четко задать агрегатные функции для каждого выводимого поля.
Рассмотрим подробнее, какие функции можно использовать в секции ИТОГИ:
Если вам нужно вывести реквизиты шапки документа (например, Контрагента, Организацию или Договор), которые одинаковы для всех строк внутри одного документа, используйте функцию МАКСИМУМ:
ВЫБРАТЬ
ВТПоступления.Док КАК Док,
ВТПоступления.Контрагент КАК Контрагент,
ВТПоступления.Сумма КАК Сумма
ИЗ
ВТПоступления КАК ВТПоступления
ИТОГИ
МАКСИМУМ(Контрагент),
СУММА(Сумма)
ПО
Док
В данном примере на уровне итогов по документу в поле "Контрагент" запишется конкретная ссылка на контрагента (так как для одного документа он уникален), а в поле "Сумма" — общая сумма всех строк этого документа.
Очень часто при настройке итогов разработчики сталкиваются с тем, что платформа наотрез отказывается рассчитывать итоги по таким полям, как Комментарий, Содержание или другим текстовым реквизитам неограниченной длины.
Попытка написать конструкцию МАКСИМУМ(Комментарий) приведет к ошибке выполнения запроса. Выясним, почему так происходит.
В СУБД (например, MS SQL Server) поля неограниченной длины хранятся как LOB-данные (Large Object) — типы вроде NVARCHAR(MAX) или TEXT. СУБД накладывает жесткие ограничения на работу с такими типами данных: к ним нельзя применять агрегатные функции, их нельзя использовать в условиях группировки СГРУППИРОВАТЬ ПО и по ним невозможно выполнять индексацию или сортировку.
Чтобы обойти это ограничение, нам необходимо привести строку неограниченной длины к строке фиксированной длины. Для этого используется функция ВЫРАЗИТЬ.
Посмотрим на пример правильного преобразования:
ВЫБРАТЬ
ЗаказПокупателя.Ссылка КАК Ссылка,
ВЫРАЗИТЬ(ЗаказПокупателя.Комментарий КАК СТРОКА(150)) КАК КомментарийОграниченный,
ЗаказПокупателя.СуммаДокумента КАК Сумма
ИЗ
Документ.ЗаказПокупателя КАК ЗаказПокупателя
ИТОГИ
МАКСИМУМ(КомментарийОграниченный),
СУММА(Сумма)
ПО
Ссылка
После того как мы явно ограничили длину комментария до 150 символов с помощью функции ВЫРАЗИТЬ, СУБД начинает воспринимать это поле как обычную строку фиксированной длины. Теперь мы без проблем можем использовать к нему функцию МАКСИМУМ в секции итогов.
Разберем по шагам важные особенности работы с функцией приведения типов, о которых необходимо помнить каждому разработчику:
ВЫРАЗИТЬ(Комментарий КАК СТРОКА(2000)). Максимально допустимая длина составляет 1024 символа. Если указать больше, запрос выдаст ошибку еще на этапе компиляции.ПОДСТРОКА(Комментарий, 1, 150) вместо ВЫРАЗИТЬ. Фирма «1С» настоятельно рекомендует использовать именно ВЫРАЗИТЬ. Дело в том, что функция ПОДСТРОКА при определенных сложных условиях (например, при использовании внутри конструкции ВЫБОР КОГДА) может заставить MS SQL Server выделить под результат буфер максимальной длины (до 4000 символов). Если в запросе много таких полей и по ним идет группировка или сортировка, это может вызвать переполнение буфера СУБД (лимит 8000 байт на строку сортировки) и привести к аварийной ошибке. Функция ВЫРАЗИТЬ жестко определяет длину поля для оптимизатора СУБД, гарантируя стабильную работу плана запроса.NULL, то конструкция ВЫРАЗИТЬ(Поле КАК СТРОКА(150)) вернет именно NULL, а не пустую строку. Для корректного отображения рекомендуется оборачивать такое выражение в функцию ЕСТЬNULL:
ЕСТЬNULL(ВЫРАЗИТЬ(ДопРеквизит КАК СТРОКА(150)), "")Если у вас в запросе не считаются итоги, выполните проверку по следующему чек-листу:
ИТОГИ не заключена в фигурные скобки {} (если вы не используете СКД или построитель программно).ПОМЕСТИТЬ).ПО), обязательно укажите агрегатные функции: СУММА — для чисел, МАКСИМУМ — для ссылок, дат и строк.ВЫРАЗИТЬ(Поле КАК СТРОКА(Длина)), где длина не превышает 1024 символа.