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