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