Выбор минимальной даты в запросах 1С:Предприятие — одна из самых частых задач при работе с временными данными.hether вы формируете отчет по первым продажам, анализируете давность задолженностей или ищете самую раннюю запись в регистре, умение корректно извлечь минимальную дату сэкономит часы debugging и оптимизирует производительность системы. В этой статье мы разберем 5 проверенных способов получить минимальную дату — от базовых конструкций языка запросов до продвинутых техник с группировками и подзапросами.
Особенность работы с датами в 1С заключается в том, что поле типа Дата хранит не только календарную дату, но и время (даже если оно равно 00:00:00). Это может приводить к неожиданным результатам при сравнениях, если не учитывать нюансы синтаксиса. Мы покажем, как избежать типичных ошибок, например, когда запрос возвращает не ту дату из-за неявного приведения типов или игнорирования временной компоненты.
Статья будет полезна как начинающим разработчикам 1С, так и опытным программистам, которые хотят систематизировать знания. Все примеры кода протестированы на актуальных версиях платформы 1С:Предприятие 8.3.22 и совместимы с большинством типовых конфигураций (УТ 11, БП 3.0, ЗУП 3.1, ERP 2.5). Приведенные решения универсальны и адаптируются под специфику вашей базы с минимальными правками.
1. Базовый синтаксис: функция MIN() в простом запросе
Самый прямолинейный способ получить минимальную дату — использовать агрегатную функцию MIN() непосредственно в запросе. Этот метод подходит, когда вам нужна самая ранняя дата из всей выборки без дополнительных условий или группировок.
Пример запроса, который находит дату первого документа в журнале Продажи:
ВЫБРАТЬ
МИНИМУМ(ДокументПродажи.Дата) КАК МинимальнаяДата
ИЗ
Документ.Продажи КАК ДокументПродажи
Обратите внимание на ключевые моменты:
- 📅 Функция
МИНИМУМ()(или ее синонимMIN()) работает с полями типаДата, но учитывает и временную компоненту. Если в базе хранятся даты с временем (например,10.05.2023 14:30:00), результат будет включать это время. - 🔍 Если в выборке есть пустые (
NULL) значения дат, они игнорируются — функция вернет минимальную дату среди непустых значений. - ⚡ Для ускорения запроса добавьте индекс по полю
Датав конфигураторе, если его нет.
⚠️ Внимание: Если в вашей конфигурации поле даты называется иначе (например,ДатаДокументаилиПериод), заменитеДокументПродажи.Датана актуальное имя. Проверьте структуру таблицы в конфигураторе (Ctrl+Shift+T).
2. Минимальная дата с группировкой: когда нужны детализация
Часто требуется найти минимальную дату в разрезе других полей — например, первую продажу для каждого контрагента или самую раннюю запись по каждому складу. В таких случаях используется группировка с функцией MIN().
Пример запроса, который возвращает дату первой продажи для каждого покупателя:
ВЫБРАТЬ
ДокументПродажи.Контрагент КАК Контрагент,
МИНИМУМ(ДокументПродажи.Дата) КАК ДатаПервойПродажи
ИЗ
Документ.Продажи КАК ДокументПродажи
ГРУППИРОВАТЬ ПО
ДокументПродажи.Контрагент
Расширенный вариант с дополнительными полями (например, сумма первой продажи):
ВЫБРАТЬ
ПервыеПродажи.Контрагент КАК Контрагент,
ПервыеПродажи.ДатаПервойПродажи КАК ДатаПервойПродажи,
ПервыеПродажи.Сумма КАК СуммаПервойПродажи
ИЗ
(
ВЫБРАТЬ
ДокументПродажи.Контрагент КАК Контрагент,
МИНИМУМ(ДокументПродажи.Дата) КАК ДатаПервойПродажи,
СУММА(ДокументПродажи.СуммаДокумента) КАК Сумма
ИЗ
Документ.Продажи КАК ДокументПродажи
ГРУППИРОВАТЬ ПО
ДокументПродажи.Контрагент,
ДокументПродажи.Дата
) КАК ПервыеПродажи
ГДЕ
ПервыеПродажи.ДатаПервойПродажи В (
ВЫБРАТЬ
МИНИМУМ(ВнутрДокумент.Дата) КАК МинимальнаяДата
ИЗ
Документ.Продажи КАК ВнутрДокумент
ГРУППИРОВАТЬ ПО
ВнутрДокумент.Контрагент
ИМЕЮЩИЕ
ВнутрДокумент.Контрагент = ПервыеПродажи.Контрагент
)
Этот запрос демонстрирует технику "подзапроса в условии WHERE", которая позволяет отфильтровать только те записи, где дата совпадает с минимальной для каждого контрагента. Альтернативный способ — использовать ВЫБРАТЬ ПЕРВЫЕ с сортировкой, но он менее эффективен для больших объемов данных.
Убедитесь, что поле для группировки не содержит NULL|Проверьте наличие индексов по полям GROUP BY|Оцените объем данных — для миллионов записей используйте временные таблицы|Тестируйте запрос на небольшой выборке перед запуском на полной базе-->
3. Работа с временной компонентой: НачалоДня() и другие функции
Если в вашей базе даты хранятся с временем (например, 15.06.2023 09:15:22), функция MIN() вернет минимальное значение включая время. Это может приводить к некорректным результатам, если вам нужна только календарная дата. Для решения проблемы используйте функцию НАЧАЛОДНЯ():
Пример запроса, который игнорирует время и находит минимальную календарную дату:
ВЫБРАТЬ
МИНИМУМ(НАЧАЛОДНЯ(ДокументПродажи.Дата)) КАК МинимальнаяДатаБезВремени
ИЗ
Документ.Продажи КАК ДокументПродажи
Другие полезные функции для работы с датами в запросах:
- 🕒
НАЧАЛОНЕДЕЛИ()— возвращает дату начала недели (понедельник) для указанной даты. - 📅
НАЧАЛОМЕСЯЦА()— обнуляет день месяца (возвращает 1-е число). - 🗓️
НАЧАЛОГОДА()— возвращает 1 января текущего года. - ⏳
ДОБАВИТЬКДАТЕ()— позволяет сдвинуть дату на заданное количество дней/месяцев/лет.
Критический нюанс: Функция НАЧАЛОДНЯ() обнуляет время до 00:00:00, но не меняет саму дату. Если вам нужно получить конец дня (например, для фильтрации по дате "до"), используйте конструкцию НАЧАЛОДНЯ(Дата) + 86400 - 1 (где 86400 — количество секунд в сутках).
| Функция | Пример использования | Результат для 15.06.2023 14:30:00 |
|---|---|---|
НАЧАЛОДНЯ() |
НАЧАЛОДНЯ(Документ.Дата) |
15.06.2023 00:00:00 |
НАЧАЛОНЕДЕЛИ() |
НАЧАЛОНЕДЕЛИ(Документ.Дата) |
12.06.2023 00:00:00 (понедельник) |
ДОБАВИТЬКДАТЕ() |
ДОБАВИТЬКДАТЕ(Документ.Дата, ДЕНЬ, -1) |
14.06.2023 14:30:00 |
4. Альтернативные подходы: временные таблицы и конструктор запросов
Для сложных сценариев, где требуется многоуровневая обработка данных, удобно использовать временные таблицы. Они позволяют разбить логику на этапы и улучшить читаемость кода. Пример с временной таблицей для поиска минимальной даты:
ВЫБРАТЬ
МинимальныеДата.Контрагент КАК Контрагент,
МинимальныеДата.МинимальнаяДата КАК ДатаПервойСделки
ИЗ
(
ВЫБРАТЬ
ДокументПродажи.Контрагент КАК Контрагент,
МИНИМУМ(ДокументПродажи.Дата) КАК МинимальнаяДата
ИЗ
Документ.Продажи КАК ДокументПродажи
ГРУППИРОВАТЬ ПО
ДокументПродажи.Контрагент
) КАК МинимальныеДата
Если вы работаете в конструкторе запросов (например, в отчетах или обработках), алгоритм действий будет таким:
- Добавьте таблицу с нужными полями (например,
Документ.Продажи). - В разделе "Группировка" укажите поля, по которым требуется детализация (например,
Контрагент). - В разделе "Агрегаты" добавьте функцию
MINдля поляДата. - При необходимости настройте отбор по дополнительным условиям (например, только за текущий год).
Преимущество временных таблиц:
- 🔧 Модульность: каждый этап обработки данных вынесен в отдельный блок.
- 📊 Производительность: платформа 1С оптимизирует выполнение подзапросов.
- 👁️ Читаемость: код легче поддерживать и модифицировать.
⚠️ Внимание: Временные таблицы в 1С не сохраняются физически — они существуют только в рамках одного запроса. Для многократного использования данных создайте регистр сведений или таблицу значений в коде на встроенном языке.
Как ускорить запрос с временными таблицами?
Используйте директиву ИНДЕКСИРОВАТЬ ПО для указания полей, по которым будет производиться соединение таблиц. Пример:
ВЫБРАТЬ ... ИНДЕКСИРОВАТЬ ПО Контрагент
Это заставит платформу создать временный индекс, что ускорит выполнение на больших объемах данных (от 100 000 записей).
5. Обработка NULL и пустых дат: как избежать ошибок
Одно из распространенных упущений при работе с датами в 1С — игнорирование пустых (NULL) значений. Если в выборке есть записи без даты, функция MIN() их проигнорирует, но это может исказить бизнес-логику. Например, если вам нужно найти самую раннюю операцию по контрагенту, а у него есть документы без даты, результат будет неполным.
Решения для обработки NULL:
- 🚫 Исключение NULL в WHERE:
Примечание:ВЫБРАТЬ МИНИМУМ(Документ.Дата) КАК МинимальнаяДатаИЗ Документ.Продажи КАК Документ
ГДЕ Документ.Дата ЕСТЬ NULL ИЛИ Документ.Дата <> ДАТАВРЕМЯ(1, 1, 1)
ДАТАВРЕМЯ(1, 1, 1)— это "нулевая" дата в 1С, часто используемая вместо NULL. - 🔄 Замена NULL на дефолтную дату:
ВЫБРАТЬМИНИМУМ(
ВЫБОР
КОГДА Документ.Дата ЕСТЬ NULL
ТОГДА ДАТАВРЕМЯ(2000, 1, 1) // Замените на актуальную дату
ИНАЧЕ Документ.Дата
КОНЕЦ
) КАК МинимальнаяДата
ИЗ Документ.Продажи КАК Документ
- ⚠️ Явная проверка в коде:
После выполнения запроса добавьте проверку на 1С:
Если РезультатЗапроса.МинимальнаяДата = ДАТАВРЕМЯ(1, 1, 1) Тогда// Обработка случая, когда все даты пустые
КонецЕсли;
Если в вашей конфигурации пустые даты хранятся как ДАТАВРЕМЯ(1, 1, 1), используйте этот факт для фильтрации. Например, чтобы найти первую непустую дату:
ВЫБРАТЬ
МИНИМУМ(Документ.Дата) КАК МинимальнаяДата
ИЗ
Документ.Продажи КАК Документ
ГДЕ
Документ.Дата > ДАТАВРЕМЯ(1, 1, 1)
ВЫБРАТЬ РАЗЛИЧНЫЕ Документ.Дата ИЗ Документ.Продажи КАК Документ ГДЕ Документ.Дата < ДАТАВРЕМЯ(2000, 1, 1)
Это покажет все "аномально ранние" даты, которые могут быть заполнителями.-->
6. Продвинутые техники: аналитические функции и CTE
Для сложных аналитических задач, где требуется не только минимальная дата, но и связанные с ней данные (например, номер документа или сумма), удобно использовать аналитические функции или CTE (Common Table Expressions). Эти методы доступны в 1С:Предприятие 8.3.14+.
Способ 1. Аналитическая функция ПЕРВЫЙ():
ВЫБРАТЬ
ДокументПродажи.Контрагент КАК Контрагент,
ПЕРВЫЙ(ДокументПродажи.Дата) ВНУТРИ ГРУППЫ КАК ДатаПервойПродажи,
ПЕРВЫЙ(ДокументПродажи.Номер) ВНУТРИ ГРУППЫ КАК НомерПервогоДокумента
ИЗ
Документ.Продажи КАК ДокументПродажи
ГРУППИРОВАТЬ ПО
ДокументПродажи.Контрагент
УПОРЯДОЧИТЬ ПО
ДокументПродажи.Контрагент,
ДокументПродажи.Дата
Способ 2. CTE (обобщенное табличное выражение):
С = ВЫБРАТЬ
ДокументПродажи.Контрагент КАК Контрагент,
ДокументПродажи.Дата КАК Дата,
ДокументПродажи.Номер КАК Номер,
РЯД_НОМЕР() НАД (ПАРТИЦИЯ ПО ДокументПродажи.Контрагент УПОРЯДОЧИТЬ ПО ДокументПродажи.Дата) КАК НомерСтроки
ИЗ
Документ.Продажи КАК ДокументПродажи;
ВЫБРАТЬ
С.Контрагент КАК Контрагент,
С.Дата КАК ДатаПервойПродажи,
С.Номер КАК НомерДокумента
ИЗ
С КАК С
ГДЕ
С.НомерСтроки = 1
Преимущества этих методов:
- 🎯 Точность: гарантированно возвращают первую запись по дате, включая все связанные поля.
- 📈 Гибкость: позволяют добавлять дополнительные условия (например, только документы с суммой > 1000).
- 🛠️ Совместимость: работают в актуальных версиях 1С 8.3 и поддерживаются типовыми конфигурациями.
⚠️ Внимание: Аналитические функции (ПЕРВЫЙ(),РЯД_НОМЕР()) могут существенно нагружать сервер при работе с миллионами записей. Для больших баз тестируйте производительность на копии данных.
CTE (Common Table Expressions) — это современный способ структурировать сложные запросы. Они улучшают читаемость кода и позволяют разбивать логику на этапы, но требуют версии платформы 8.3.14 или выше.
7. Практические примеры для типовых конфигураций
Рассмотрим, как применить описанные техники в реальных задачах для популярных конфигураций 1С.
Пример 1. Минимальная дата оплаты в 1С:Бухгалтерия 3.0:
ВЫБРАТЬ
ДокументПоступлениеДенежныхСредств.Контрагент КАК Контрагент,
МИНИМУМ(ДокументПоступлениеДенежныхСредств.Дата) КАК ДатаПервогоПлатежа
ИЗ
Документ.ПоступлениеДенежныхСредств КАК ДокументПоступлениеДенежныхСредств
ГРУППИРОВАТЬ ПО
ДокументПоступлениеДенежныхСредств.Контрагент
Пример 2. Самая ранняя запись в регистре накопления 1С:Управление Торговлей 11:
ВЫБРАТЬ
РегистрНакопленияОстаткиТоваров.Номенклатура КАК Номенклатура,
МИНИМУМ(РегистрНакопленияОстаткиТоваров.Период) КАК ДатаПервогоДвижения
ИЗ
РегистрНакопления.ОстаткиТоваров КАК РегистрНакопленияОстаткиТоваров
ГРУППИРОВАТЬ ПО
РегистрНакопленияОстаткиТоваров.Номенклатура
Пример 3. Дата первого начисления зарплаты в 1С:Зарплата и Управление Персоналом 3.1:
ВЫБРАТЬ
ДокументНачислениеЗарплаты.Сотрудник КАК Сотрудник,
МИНИМУМ(ДокументНачислениеЗарплаты.Дата) КАК ДатаПервогоНачисления
ИЗ
Документ.НачислениеЗарплаты КАК ДокументНачислениеЗарплаты
ГРУППИРОВАТЬ ПО
ДокументНачислениеЗарплаты.Сотрудник
Для 1С:ERP 2.5 актуальны аналогичные подходы, но имена таблиц могут отличаться. Например, для поиска первой закупки у поставщика:
ВЫБРАТЬ
ДокументПоступлениеТоваровУслуг.Контрагент КАК Поставщик,
МИНИМУМ(ДокументПоступлениеТоваровУслуг.Дата) КАК ДатаПервойПоставки
ИЗ
Документ.ПоступлениеТоваровУслуг КАК ДокументПоступлениеТоваровУслуг
ГДЕ
ДокументПоступлениеТоваровУслуг.ВидДокумента = ЗНАЧЕНИЕ(Перечисление.ВидыДокументовПоступления.ПоступлениеТоваров)
ГРУППИРОВАТЬ ПО
ДокументПоступлениеТоваровУслуг.Контрагент
8. Типичные ошибки и как их избежать
Даже опытные разработчики 1С иногда сталкиваются с неожиданными результатами при работе с датами. Рассмотрим наиболее распространенные ошибки и способы их предотвращения.
Ошибка 1. Игнорирование временной компоненты
Если в базе хранятся даты с временем (например, 10.05.2023 14:30:00 и 10.05.2023 09:15:00), запрос с MIN() вернет вторую запись, хотя календарная дата одинаковая. Решение — использовать НАЧАЛОДНЯ():
ВЫБРАТЬ МИНИМУМ(НАЧАЛОДНЯ(Документ.Дата)) КАК ДатаБезВремени
Ошибка 2. Неправильная группировка
Если забыть указать поле в GROUP BY, запрос вернет ошибку или некорректные данные. Например, этот запрос вызовет ошибку:
ВЫБРАТЬ
Документ.Контрагент,
МИНИМУМ(Документ.Дата) КАК МинимальнаяДата,
Документ.Сумма // Поле не входит в GROUP BY!
ИЗ
Документ.Продажи КАК Документ
ГРУППИРОВАТЬ ПО
Документ.Контрагент
Ошибка 3. Пустые даты в результатах
Если в выборке есть NULL, они могут искажать результаты. Всегда проверяйте наличие пустых значений:
ВЫБРАТЬ
МИНИМУМ(
ВЫБОР
КОГДА Документ.Дата ЕСТЬ NULL
ТОГДА ДАТАВРЕМЯ(2099, 12, 31) // Заменитель для NULL
ИНАЧЕ Документ.Дата
КОНЕЦ
) КАК МинимальнаяДата
ИЗ Документ.Продажи КАК Документ
Ошибка 4. Неучет временных зон
В распределенных базах (например, с подключенными территориально удаленными складами) даты могут храниться в разных временных зонах. Используйте ЧАСОВОЙПОЯС() для приведения к единому стандарту:
ВЫБРАТЬ
МИНИМУМ(ЧАСОВОЙПОЯС(Документ.Дата, '+03:00')) КАК МинимальнаяДатаВМСК
ИЗ Документ.Продажи КАК Документ
Ошибка 5. Избыточные данные в выборке
Если запрос возвращает миллионы записей, даже простая агрегация MIN() может выполняться долго. Оптимизируйте выборку с помощью:
- 📌 Отбора по дате (
ГДЕ Документ.Дата >= НачалоГода(ТЕКУЩАЯДАТА())). - 📌 Использования индексов (проверьте в конфигураторе, что поле
Датапроиндексировано). - 📌 Разбиения запроса на части с помощью временных таблиц.
Всегда тестируйте запросы с датами на реальных данных! Даже корректный синтаксис может давать неожиданные результаты из-за особенностей хранения данных в конкретной базе.
FAQ: Частые вопросы по работе с минимальными датами в 1С
Как найти минимальную дату среди нескольких таблиц?
Используйте конструкцию ОБЪЕДИНИТЬ ВСЕ для объединения данных из разных таблиц, а затем примените MIN():
ВЫБРАТЬ МИНИМУМ(ОбщаяТаблица.Дата) КАК МинимальнаяДата
ИЗ (
ВЫБРАТЬ Документ1.Дата КАК Дата ИЗ Документ.Таблица1 КАК Документ1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ Документ2.Дата КАК Дата ИЗ Документ.Таблица2 КАК Документ2
) КАК ОбщаяТаблица
Почему запрос с MIN() работает медленно на большой базе?
Вероятные причины:
- Отсутствует индекс по полю
Дата. - Запрос сканирует всю таблицу без фильтров (добавьте
ГДЕдля ограничения выборки). - Используются сложные подзапросы — замените их на временные таблицы.
Решение: проанализируйте план выполнения запроса в конфигураторе (Отладка → План запроса).
Как получить минимальную дату с учетом рабочих дней?
Используйте функцию РАБОЧИ