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