Как сравнить два поля в запросе 1С, если одно из них — дата, а другое — строка?

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

В процессе разработки на платформе 1С программисты часто сталкиваются с ситуацией, когда необходимо выбрать данные из базы, основываясь на сравнении двух дат. В идеальных условиях, когда оба поля имеют тип Дата, задача решается одним простым условием в секции ГДЕ. Однако на практике встречаются конфигурации (особенно самописные или старые), где даты по какой-то причине хранятся в строковом формате. Рассмотрим подробно, почему это становится проблемой и как правильно выйти из такой ситуации.

Базовый случай: Сравнение двух полей типа Дата

Если в вашей таблице оба реквизита имеют корректный тип Дата, то использование операторов сравнения (больше, меньше, равно) не вызывает никаких затруднений. Система 1С на уровне СУБД выполняет стандартное сравнение значений.

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


ВЫБРАТЬ
    Контрагенты.Ссылка КАК Ссылка,
    Контрагенты.Дата1 КАК Дата1,
    Контрагенты.Дата2 КАК Дата2
ИЗ
    Справочник.Контрагенты КАК Контрагенты
ГДЕ
    Контрагенты.Дата1 > Контрагенты.Дата2

Проанализируем этот код: здесь мы обращаемся к справочнику Контрагенты и накладываем условие, которое напрямую сравнивает значения в двух колонках. Это работает быстро, так как индексы по датам (если они установлены) эффективно используются SQL-сервером.

Проблема: Сравнение разных типов (Дата и Строка)

Ситуация кардинально меняется, когда выясняется, что одно из полей — Строка. В языке запросов 1С отсутствует неявное приведение типов. Это означает, что если мы попытаемся выполнить условие Дата1 > Строка2, система выдаст ошибку "Несовместимые типы".

Выясним причину, по которой нельзя просто использовать функцию ВЫРАЗИТЬ(). В 1С эта функция предназначена для:

  1. Приведения ссылочных типов к конкретному типу (для доступа к полям через точку).
  2. Ограничения длины строк или точности чисел.

К сожалению, ВЫРАЗИТЬ(ПолеСтрока КАК ДАТА) технически невозможно. В языке запросов 1С нет штатного способа превратить произвольную строку в дату "на лету".

Риски лексикографического сравнения строк

Иногда разработчики пытаются обойти проблему, приводя оба поля к строке, чтобы запрос хотя бы выполнился. Однако здесь нас подстерегает ловушка лексикографического (посимвольного) сравнения. Посмотрим на примере:

Пусть у нас есть две даты в строковом формате ДД.ММ.ГГГГ:

Для человека очевидно, что первая дата больше. Но для системы при посимвольном сравнении строка "31.12.2023" окажется БОЛЬШЕ, чем "01.01.2024", потому что первый символ "3" больше первого символа "0".

Сравнение дат в виде строк корректно работает только в том случае, если они записаны в формате ГГГГММДДЧЧММСС. Если формат в вашей базе иной — результаты запроса будут ложными.

Правильное решение: Исправление структуры метаданных

Если мы обнаружили, что дата хранится в строке, лучшим решением будет рефакторинг. Разберем по шагам, как безопасно изменить тип поля, не потеряв накопленные данные.

Шаг 1. Создание нового реквизита. Добавим в справочник новый реквизит, например, Дата2_Новая с типом Дата (состав даты: Дата и время).

Шаг 2. Перенос данных. Напишем небольшую обработку, которая сконвертирует строковые значения в тип Дата. Рассмотрим пример кода такой обработки:


Выборка = Справочники.Контрагенты.Выбрать();
Пока Выборка.Следующий() Цикл
    Объект = Выборка.ПолучитьОбъект();
    // Попытаемся преобразовать строку в дату. 
    // Предположим, строка в формате "ДД.ММ.ГГГГ" или стандартном 1С
    Попытка
        Объект.Дата2_Новая = Дата(Объект.Дата2_Строка); 
        Объект.Записать();
    Исключение
        ЗаписьЖурналаРегистрации("Ошибка конвертации", УровеньЖурналаРегистрации.Ошибка,,, "Объект: " + Объект.Ссылка);
    КонецПопытки;
КонецЦикла;

Шаг 3. Замена ссылок в коде и запросах. После того как данные перенесены, необходимо найти все упоминания старого строкового реквизита и заменить их на новый реквизит типа Дата.

Шаг 4. Удаление старого реквизита. Только убедившись, что всё работает корректно, можно удалить старое строковое поле в конфигураторе.

Важные нюансы: NULL и пустые даты

При сравнении полей в запросах всегда нужно учитывать возможность отсутствия данных. Проанализируем две важные ситуации:

  1. Пустые значения. В 1С "пустая дата" (01.01.0001) — это самое маленькое значение. Если в вашем запросе одно из полей может быть не заполнено, условие Дата1 > Дата2 сработает, но может не соответствовать бизнес-логике. Рекомендуется явно проверять заполненность: ГДЕ Контрагенты.Дата1 <> ДАТАВРЕМЯ(1, 1, 1).
  2. Значения NULL. Если данные выбираются через ЛЕВОЕ СОЕДИНЕНИЕ, то в случае отсутствия записи во второй таблице поле примет значение NULL. Любое сравнение с NULL всегда дает результат "Ложь". Чтобы избежать этого, используем функцию ЕСТЬNULL.

Посмотрим на пример безопасного сравнения с учетом этих нюансов:


ВЫБРАТЬ
    Контрагенты.Ссылка
ИЗ
    Справочник.Контрагенты КАК Контрагенты
ГДЕ
    ЕСТЬNULL(Контрагенты.Дата1, ДАТАВРЕМЯ(1, 1, 1)) > ЕСТЬNULL(Контрагенты.Дата2, ДАТАВРЕМЯ(1, 1, 1))

Обходной путь: Временные таблицы

Если же изменить структуру базы прямо сейчас нельзя (например, конфигурация на поддержке и изменения запрещены), задачу можно решить в два этапа через временные таблицы в коде 1С. Мы сначала выбираем данные в таблицу значений, программно добавляем колонку нужного типа, заполняем её, а затем передаем эту таблицу обратно в менеджер временных таблиц для финального запроса. Но помните, что это решение — "костыль", который существенно замедляет работу системы по сравнению с правильной структурой базы данных.

← На главную