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