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