Почему в отчете СКД не происходит упорядочивание при использовании ВЫБРАТЬ ПЕРВЫЕ

Программист 1С v8.3 (Управляемые формы)
← На главную

При разработке сложных отчетов на Системе компоновки данных (СКД) программисты часто сталкиваются с ситуацией, когда запрос, идеально работающий в «Консоли запросов», выдает неверные данные в самом отчете. Типичный пример: нам нужно получить последнюю запись (например, последний пробег или последнее ТО) с помощью конструкции ВЫБРАТЬ ПЕРВЫЕ 1 с сортировкой по дате во временной таблице. Но как только мы формируем отчет, данные «плывут», и выбирается совсем не та запись, которую мы ожидали. Рассмотрим подробнее, почему это происходит и как это исправить.

Суть проблемы и механизм работы СКД

Разберем ситуацию на примере исходного запроса. В нем создается временная таблица Выработка, где выбирается одна первая запись с левым соединением по условию даты и упорядочиванием по полю Период:


ВЫБРАТЬ ПЕРВЫЕ 1
    ПоследниеТО.ОсновноеСредство,
    ПоследниеТО.ДатаКон,
    ПоказанияСчетчикаОС.ПоказаниеСпидометра,
    ПоказанияСчетчикаОС.ПоказаниеСчетчикаМотоЧасов,
    ПоказанияСчетчикаОС.Период КАК Период,
    ПоследниеТО.Ссылка
ПОМЕСТИТЬ Выработка
ИЗ
    ПоследниеТО КАК ПоследниеТО
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПоказанияСчетчикаОС КАК ПоказанияСчетчикаОС
        ПО ПоследниеТО.ОсновноеСредство = ПоказанияСчетчикаОС.ОсновноеСредство
            И (ПоследниеТО.ДатаКон >= ПоказанияСчетчикаОС.Период)
УПОРЯДОЧИТЬ ПО
    Период УБЫВ

Проблема заключается в том, что СКД — это не просто «исполнитель» вашего запроса. Это мощный генератор, который перестраивает исходный текст запроса перед отправкой на сервер СУБД. Процесс компоновки макета анализирует, какие поля пользователь выбрал в настройках отчета (вкладка «Выбранные поля»). Если поле Период используется для сортировки внутри временной таблицы, но не выведено пользователем в отчет, оптимизатор СКД может решить, что это поле лишнее, и исключить его из выборки. Без поля, по которому идет УПОРЯДОЧИТЬ ПО, конструкция ВЫБРАТЬ ПЕРВЫЕ теряет смысл, так как СУБД вернет любую произвольную запись, которая первой попалась в индексе или таблице.

Решение 1: Использование флага «Обязательное»

Это самый правильный и «чистый» способ решения проблемы с точки зрения разработки. В схеме компоновки данных мы можем явно указать системе, что определенные поля нельзя исключать из запроса, даже если их нет в финальном отчете. Проанализируем, как это сделать:

  1. Откройте схему компоновки данных (файл .dcs).
  2. Перейдите на закладку Наборы данных.
  3. Найдите поле, по которому происходит упорядочивание (в нашем случае это Период).
  4. В колонке Обязательное установите галочку.

Установка этого флага гарантирует, что КомпоновщикМакета всегда будет включать Период в секцию SELECT итогового SQL-запроса. Следовательно, инструкция УПОРЯДОЧИТЬ ПО Период УБЫВ отработает корректно, и ВЫБРАТЬ ПЕРВЫЕ 1 вернет именно ту строку, которая нам нужна.

Решение 2: Отключение автоупорядочивания

Выясним причину, почему иногда ручная сортировка в запросе игнорируется. В СКД по умолчанию включено свойство Автоупорядочивание. Система пытается помочь разработчику и автоматически добавляет в секцию ORDER BY ссылочные поля и периоды для обеспечения стабильности результата. Однако при работе с ВЫБРАТЬ ПЕРВЫЕ во временных таблицах это «помощь» может мешать.

Чтобы система не вмешивалась в логику сортировки вашего запроса:

  1. На закладке Наборы данных под текстом запроса найдите свойства набора данных.
  2. Снимите галочку Автоупорядочивание.

После этого СКД будет использовать только те инструкции УПОРЯДОЧИТЬ ПО, которые вы явно прописали в тексте запроса. Но помните: в этом случае вам придется самостоятельно настроить сортировку в настройках самого отчета, если она требуется пользователю.

Решение 3: Изменение логики получения среза данных

Использование ВЫБРАТЬ ПЕРВЫЕ 1 внутри временной таблицы с левым соединением — это попытка вручную реализовать получение «среза последних» на определенную дату (дату документа). Рассмотрим, как можно переписать этот фрагмент, чтобы он был более устойчив к оптимизациям СКД.

Вместо использования ПЕРВЫЕ 1, мы можем сначала найти максимальную дату (период) для каждого основного средства, а затем соединиться с данными регистра по этой дате. Посмотрим на пример такой логики:


// Сначала находим максимальные даты для каждой группы
ВЫБРАТЬ
    ПоследниеТО.Ссылка КАК РемонтныйЛист,
    МАКСИМУМ(ПоказанияСчетчикаОС.Период) КАК МаксПериод
ПОМЕСТИТЬ МаксимальныеДаты
ИЗ
    ПоследниеТО КАК ПоследниеТО
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ПоказанияСчетчикаОС КАК ПоказанияСчетчикаОС
    ПО ПоследниеТО.ОсновноеСредство = ПоказанияСчетчикаОС.ОсновноеСредство
    И ПоследниеТО.ДатаКон >= ПоказанияСчетчикаОС.Период
СГРУППИРОВАТЬ ПО
    ПоследниеТО.Ссылка;

////////////////////////////////////////////////////////////////////////////////
// Теперь получаем сами данные по найденным датам
ВЫБРАТЬ
    ПоследниеТО.ОсновноеСредство,
    ПоказанияСчетчикаОС.ПоказаниеСпидометра,
    МаксимальныеДаты.МаксПериод КАК Период
ИЗ
    ПоследниеТО КАК ПоследниеТО
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ МаксимальныеДаты КАК МаксимальныеДаты
    ПО ПоследниеТО.Ссылка = МаксимальныеДаты.РемонтныйЛист
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ПоказанияСчетчикаОС КАК ПоказанияСчетчикаОС
    ПО ПоследниеТО.ОсновноеСредство = ПоказанияСчетчикаОС.ОсновноеСредство
    И МаксимальныеДаты.МаксПериод = ПоказанияСчетчикаОС.Период

Такой подход считается более надежным в СКД, так как он не полагается на физический порядок строк в таблице, а использует четкую математическую агрегацию МАКСИМУМ(Период). Компоновщик СКД не сможет «оптимизировать» группировку так, чтобы она выдала неверный результат.

Решение 4: Роли полей и виртуальные таблицы

Проанализируем ситуацию с виртуальными таблицами. Если ваша задача — получить данные на &ДатаКон, эффективнее использовать виртуальную таблицу СрезПоследних. Если же вам нужен срез на дату каждого конкретного документа (как в примере), то СКД позволяет передавать параметры в виртуальные таблицы динамически.

Также важно проверить Роли полей на соответствующей вкладке в СКД. Для поля Период должна быть указана роль «Период». Это дает понять системе компоновки, что данное поле имеет особое значение для хронологии данных, и оно будет обрабатываться более аккуратно при генерации запроса.

Почему «Просто добавить в выбранные поля» — не лучший вариант

Как советовали в сообщении 1 и 2 на форуме, можно просто заставить пользователя (или программно) выбрать поле Период в настройках отчета. Но это плохая практика по нескольким причинам:

Поэтому использование свойства Обязательное в конфигураторе является профессиональным стандартом решения данной проблемы.

Резюме по шагам

Если вы столкнулись с тем, что УПОРЯДОЧИТЬ ПО не работает внутри запроса СКД:

  1. Проверьте, используется ли поле сортировки в финальной выборке. Если нет — установите для него флаг Обязательное.
  2. Убедитесь, что Автоупорядочивание не перекрывает вашу логику (попробуйте отключить его для теста).
  3. По возможности откажитесь от конструкции ВЫБРАТЬ ПЕРВЫЕ во временных таблицах в пользу вложенных запросов с агрегатной функцией МАКСИМУМ.
  4. Всегда проверяйте итоговый запрос, который СКД отправляет в СУБД, используя консоль компоновки данных или инструменты отладки (например, КонсольЗапросов с возможностью просмотра текста запроса макета компоновки).

Следуя этим правилам, мы обеспечим стабильность и точность данных в наших отчетах вне зависимости от того, какие настройки выберет конечный пользователь.

← На главную