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