При работе с системой 1С мы регулярно сталкиваемся с необходимостью извлечения самой актуальной информации из регистров сведений. Зачастую для этой цели идеально подходит виртуальная таблица СрезПоследних. Однако, как показывает практика и опыт разработчиков, бывают ситуации, когда стандартный срез последних не дает нужного результата. Это происходит, когда определение "последней" записи требует не только учета максимального периода, но и выполнения дополнительных, специфических условий.
Рассмотрим конкретный сценарий: нам требуется найти последнюю запись по периоду, но только среди тех, которые соответствуют определенному критерию. В нашем случае это условие, что поле ДействуетДо должно быть пустым.
Представьте, что у нас есть регистр сведений КадроваяИсторияСотрудников, который содержит информацию о перемещениях сотрудников. Этот регистр имеет следующие ключевые поля:
Период: Дата, с которой действует запись.Сотрудник: Ссылка на физическое лицо.Должность: Занимаемая должность.ДействуетДо: Дата окончания действия записи (может быть пустой).Для примера, у нас есть следующие записи для одного сотрудника, "Иванов Иван":
ДействуетДо пусто)ДействуетДо заполнено)Наша задача — получить последнюю запись для каждого сотрудника, у которой поле ДействуетДо является пустым. В системе 1С пустое значение даты обычно представляется как ДАТАВРЕМЯ(1, 1, 1).
Если мы попробуем использовать стандартный СрезПоследних без дополнительной логики, он, скорее всего, вернет запись с "Гл.Специалистом" (если ее период, 03.03.2025, является самым поздним), независимо от того, что ее поле ДействуетДо заполнено. А нам нужна именно запись со "Специалистом", поскольку она является последней по периоду, отвечающей условию "ДействуетДо пусто".
Давайте подробнее разберем, почему виртуальная таблица СрезПоследних, несмотря на свою полезность, не всегда способна решить такую задачу:
СрезПоследних: Эта виртуальная таблица по умолчанию выбирает самую актуальную (с максимальным значением поля Период) запись для каждой уникальной комбинации измерений регистра на указанный момент времени. Если после записи с пустым ДействуетДо существует более поздняя запись с заполненным ДействуетДо, то СрезПоследних (без или с некорректно примененным отбором) выберет именно эту, более позднюю запись, поскольку ее Период будет больше.СрезПоследних с условием в параметрах, например, так:
ИЗ РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&ДатаСреза, ДействуетДо = ДАТАВРЕМЯ(1, 1, 1)) КАК КадроваяИсторияСотрудников
Однако, даже такой подход не всегда работает корректно и может приводить к ошибкам, например, "Поле не найдено", как было в одном из обсуждений. Это может происходить из-за того, что механизм СрезПоследних прежде всего ориентирован на выборку по максимальному периоду для измерений, а сложное условие на ресурс может интерпретироваться иначе или быть неприменимым напрямую в контексте среза. Пользователь в сообщении 8 подтвердил, что при такой попытке возникает ошибка.
Таким образом, для нашей задачи, где "последняя" запись определяется не только максимальным периодом, но и строгим дополнительным условием (пустое поле ДействуетДо), нам потребуется более гибкий подход.
Этот метод является одним из наиболее надежных и гибких способов решения нашей задачи. Мы разделим процесс получения данных на два логических этапа: сначала мы найдем максимальный период для записей, которые удовлетворяют нашему условию, а затем соединимся с основным регистром, чтобы извлечь полную информацию об этих записях.
На первом этапе мы создадим временную таблицу. Мы назовем ее, например, втСрезИсторииСотрудников. В эту таблицу мы выберем для каждого уникального сотрудника максимальное значение поля Период. Ключевой момент здесь заключается в том, что этот максимум мы ищем строго среди тех записей, где поле ДействуетДо равно пустому значению (т.е., ДАТАВРЕМЯ(1, 1, 1)).
Этот запрос эффективно группирует данные по сотруднику и позволяет нам определить самый поздний момент времени, когда сотрудник соответствовал нашему критерию "ДействуетДо пусто".
ВЫБРАТЬ
МАКСИМУМ(КадроваяИсторияСотрудников.Период) КАК Период,
КадроваяИсторияСотрудников.Сотрудник КАК Сотрудник
ПОМЕСТИТЬ втСрезИсторииСотрудников
ИЗ
РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИсторияСотрудников
ГДЕ
КадроваяИсторияСотрудников.ДействуетДо = ДАТАВРЕМЯ(1, 1, 1)
СГРУППИРОВАТЬ ПО
КадроваяИсторияСотрудников.Сотрудник
После выполнения этого шага мы получим временную таблицу, в которой для каждого Сотрудника будет указан один Период – это будет самая поздняя дата, когда для данного сотрудника существовала запись с пустым полем ДействуетДо.
Теперь, когда у нас есть точно определенный список "ключевых" периодов для каждого сотрудника (согласно нашему условию), мы можем получить полную информацию из регистра сведений. Мы выполним внутреннее соединение (ВНУТРЕННЕЕ СОЕДИНЕНИЕ) нашей временной таблицы втСрезИсторииСотрудников с основной таблицей регистра РегистрСведений.КадроваяИсторияСотрудников.
Условие для соединения будет двойным: нам необходимо совпадение как Сотрудника, так и найденного на первом шаге Периода. Это условие гарантирует, что мы выберем именно те записи, которые мы идентифицировали как "последние по условию".
;//////////////////////////////////////////////////////////////
ВЫБРАТЬ
КадроваяИсторияСотрудников.Период КАК Период,
КадроваяИсторияСотрудников.Сотрудник КАК Сотрудник,
КадроваяИсторияСотрудников.Должность КАК Должность,
КадроваяИсторияСотрудников.ДействуетДо КАК ДействуетДо
ИЗ
втСрезИсторииСотрудников КАК втСрезИсторииСотрудников
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИсторияСотрудников
ПО
втСрезИсторииСотрудников.Сотрудник = КадроваяИсторияСотрудников.Сотрудник
И втСрезИсторииСотрудников.Период = КадроваяИсторияСотрудников.Период
В результате выполнения второго запроса мы получим финальный набор записей. Для каждого сотрудника в этом наборе будет выбрана только одна запись — та, которая является последней по полю Период, и при этом ее поле ДействуетДо было пустым.
Этот двухэтапный подход является чрезвычайно гибким и мощным инструментом. Он позволяет нам определить "последнюю" запись по практически любым, даже самым сложным, критериям, которые не всегда могут быть учтены стандартными механизмами виртуальных таблиц срезов. Это решение идеально подходит для использования в Системе Компоновки Данных (СКД), где мы можем объединить эти два запроса для построения отчетов.
Помимо рассмотренного выше универсального запроса, в 1С существуют и другие способы работы с последними записями регистров сведений. Давайте кратко рассмотрим их и поймем, почему они могут быть неприменимы в нашей конкретной ситуации.
ПолучитьПоследнее()Если нам требуется получить данные только для одной конкретной комбинации измерений регистра (например, для одного определенного сотрудника), мы можем воспользоваться методом встроенного языка 1С РегистрыСведений.<ИмяРегистра>.ПолучитьПоследнее(<Дата>, <Отбор>). Этот метод возвращает структуру значений ресурсов самой поздней записи регистра сведений на указанную дату, которая соответствует заданным значениям измерений в параметре Отбор.
// Пример использования в коде 1С
Отбор = Новый Структура;
Отбор.Вставить("Сотрудник", МойСотрудник); // Отбираем по конкретному сотруднику
Отбор.Вставить("ДействуетДо", ДатаВремя(1,1,1)); // Добавляем наше условие
ПоследниеДанные = РегистрыСведений.КадроваяИсторияСотрудников.ПолучитьПоследнее(ТекущаяДата(), Отбор);
Если ПоследниеДанные <> Неопределено Тогда
Сообщить("Последняя должность: " + ПоследниеДанные.Должность);
// Доступ к другим ресурсам, например: ПоследниеДанные.Период
Иначе
Сообщить("Данные не найдены.");
КонецЕсли;
Ограничение: Мы должны понимать, что метод ПолучитьПоследнее() подходит в случаях, когда нам нужна информация о последней записи для одного объекта (например, конкретного сотрудника) в определенный момент времени. Он не предназначен для массового получения "последних" записей по условию для *всех* элементов измерения (всех сотрудников) за один раз в рамках запроса к базе данных.
ВЫБРАТЬ ПЕРВЫЕ 1 с УПОРЯДОЧИТЬ ПОДля получения одной "самой последней" записи во всем регистре, которая удовлетворяет определенному условию (без привязки к конкретным измерениям, как в случае с каждым сотрудником), можно применить конструкцию ВЫБРАТЬ ПЕРВЫЕ 1 в запросе:
ВЫБРАТЬ ПЕРВЫЕ 1
КадроваяИсторияСотрудников.Период,
КадроваяИсторияСотрудников.Сотрудник,
КадроваяИсторияСотрудников.Должность,
КадроваяИсторияСотрудников.ДействуетДо
ИЗ
РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИсторияСотрудников
ГДЕ
КадроваяИсторияСотрудников.ДействуетДо = ДАТАВРЕМЯ(1, 1, 1)
УПОРЯДОЧИТЬ ПО
КадроваяИсторияСотрудников.Период УБЫВ
Ограничение: Этот метод вернет только одну запись — ту, которая является самой последней по периоду из всего множества записей, удовлетворяющих заданному условию. Он не подходит для нашей задачи, когда нам нужна последняя запись для *каждого* уникального измерения (в нашем случае, для каждого сотрудника). Для такой массовой выборки этот способ не применим напрямую.
Итак, мы подробно рассмотрели, как эффективно выбирать последнюю запись из регистра сведений по сложному условию, когда стандартные механизмы 1С, такие как виртуальная таблица СрезПоследних, оказываются недостаточными.
Мы выяснили, что метод с использованием временной таблицы и последующим внутренним соединением с основным регистром является наиболее гибким и точным инструментом для решения подобных задач. Он позволяет нам определить "последнюю" запись по любым, даже самым изощренным критериям, которые могут выходить за рамки простой выборки по максимальному периоду.
В конечном итоге, выбор конкретного метода должен зависеть от специфики вашей задачи:
ПолучитьПоследнее() будет более оптимальным.Понимание этих подходов и их ограничений позволит вам писать более эффективные и точные запросы в 1С, а также успешно справляться с нестандартными задачами выборки данных.