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