Как в 1С отобрать данные по периоду, если дата хранится в строковом поле регистра сведений?

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

В практике разработки на платформе 1С:Предприятие часто возникают ситуации, когда данные в информационной базе хранятся в неоптимальном виде. Типичный пример — запись даты и времени события внутри строкового ресурса или комментария. Когда возникает задача построить отчет или сделать выборку за определенный период, разработчик сталкивается с серьезным ограничением: язык запросов 1С не умеет преобразовывать произвольную строку в тип Дата "на лету".

В этой статье мы подробно разберем, почему стандартный конструктор запросов не справляется с этой задачей напрямую, и проанализируем пять эффективных способов решения проблемы — от простых "хитростей" в запросе до правильного рефакторинга структуры базы данных.

Почему нельзя просто использовать стандартный запрос?

Для начала выясним причину сложности. В SQL-системах существуют функции CAST или CONVERT, которые позволяют интерпретировать текст как дату. Однако в языке запросов 1С такие функции для типа Дата отсутствуют. Более того, вы не можете вызвать функцию общего модуля внутри запроса. Если поле Notes имеет тип Строка, то для системы это просто набор символов. Сравнение "01.01.2023" > "25.12.2022" с точки зрения строк вернет Ложь, так как символ "0" меньше символа "2", хотя хронологически первая дата позже второй.

Рассмотрим по шагам, как можно обойти эти ограничения.

Решение 1: Манипуляция подстроками (Метод для строго фиксированного формата)

Если формат записи в вашем регистре строго определен (например, дата всегда занимает первые 10 символов в виде ДД.ММ.ГГГГ), мы можем прибегнуть к технической хитрости. Мы преобразуем строку в формат ГГГГММДД прямо в запросе. Такой формат строк корректно сортируется и сравнивается по порядку.

Проанализируем ситуацию: нам нужно выделить год, месяц и день, а затем склеить их. В конструкторе запросов для этого используется функция ПОДСТРОКА.

Пример логики формирования условия в запросе:


ВЫБРАТЬ
    Регистр.Notes КАК Заметка
ИЗ
    РегистрСведений.СервернаяИнформация КАК Регистр
ГДЕ
    ПОДСТРОКА(Регистр.Notes, 7, 4) + 
    ПОДСТРОКА(Регистр.Notes, 4, 2) + 
    ПОДСТРОКА(Регистр.Notes, 1, 2) МЕЖДУ "20230101" И "20231231"

Важные моменты этого метода:

Внимание: этот метод крайне медленный на больших объемах данных, так как СУБД не сможет использовать индексы и будет вынуждена сканировать всю таблицу целиком.

Решение 2: Использование Системы компоновки данных (СКД)

Если ваша задача — создать отчет, то возможности расширяются благодаря "Вычисляемым полям" в СКД. В отличие от чистого запроса, в выражении вычисляемого поля СКД можно использовать функции встроенного языка 1С.

Разберем алгоритм реализации:

  1. Создадим в общем модуле функцию ПолучитьДатуИзСтроки(ВходнаяСтрока). Она должна быть экспортной и находиться в модуле с флагом Сервер.
  2. Функция будет парсить строку xx.xx.xxxx/xx.xx.xx и возвращать реальный объект типа Дата.
  3. В схеме компоновки данных на закладке Вычисляемые поля добавим новое поле (например, РеальнаяДата).
  4. В колонке Выражение напишем: ОбщийМодуль.ПолучитьДатуИзСтроки(Notes).

Теперь в настройках отчета вы сможете использовать стандартный отбор по полю РеальнаяДата, выбирая периоды, кварталы или конкретные дни. Система сама применит преобразование при формировании результата.

Решение 3: Рефакторинг структуры (Профессиональный подход)

Как справедливо отмечалось в обсуждении, хранение даты в строке — это архитектурная ошибка. Если информация о включении/выключении серверов важна для аналитики, ее нужно типизировать. Рассмотрим, как правильно изменить структуру регистра, не теряя текущие данные в поле Notes.

Рекомендуемые действия:

  1. Добавьте в регистр новое Измерение с именем ДатаСобытия и типом Дата (состав даты: Дата и время).
  2. Использование именно Измерения (а не ресурса) позволит платформе автоматически создать индекс по этому полю. Запросы по периоду станут мгновенными.
  3. Напишите обработку, которая один раз прочитает весь регистр, распарсит строки из Notes и запишет полученные значения в новое поле ДатаСобытия.

Для автоматизации заполнения в будущем, используйте обработчик события ПередЗаписью в модуле набора записей регистра:


Процедура ПередЗаписью(Отказ, Замещение)
    Для Каждого Запись Из ЭтотОбъект Цикл
        // Здесь вызываем вашу функцию парсинга
        Запись.ДатаСобытия = ПарсингДатыИзТекста(Запись.Notes);
    КонецЦикла;
КонецПроцедуры

Решение 4: Промежуточная выгрузка в Таблицу Значений

Если данных в регистре немного (до 10-20 тысяч строк), и вы не можете менять структуру, можно воспользоваться программной обработкой результата запроса. Мы выберем все данные, обработаем их в коде и вернем в запрос через временную таблицу.

Посмотрим на пример реализации:

  1. Выполняем простой запрос: ВЫБРАТЬ Notes, Сервер ИЗ РегистрСведений.Серверы.
  2. Выгружаем результат в ТаблицаЗначений.
  3. Добавляем в таблицу колонку ДатаСобытия с типом Дата.
  4. В цикле обходим таблицу, заполняем новую колонку, используя функции Лев(), Сред() и Дата().
  5. Если нужно снова использовать эти данные в сложном запросе, передаем модифицированную таблицу как параметр запроса: МенеджерВременныхТаблиц.

Этот метод позволяет гибко работать с данными, но требует осторожности при работе с памятью сервера приложений.

Решение 5: Механизм полнотекстового поиска

В редких случаях, когда поиск ведется по огромному массиву текстовых данных и важна скорость, можно задействовать Полнотекстовый поиск (ПП). Если для ресурса Notes включено индексирование полнотекстового поиска, вы можете искать фрагменты дат как текст. Однако этот метод не позволит задать строгий период "от и до" в понимании дат, он лишь поможет быстро найти строки, содержащие определенный год или месяц.

Итоги и рекомендации

Выясним, какой метод выбрать именно вам:

Помните, что работа со строками в запросах — это всегда компромисс между удобством разработки и скоростью работы программы. Старайтесь хранить данные в тех типах, которые соответствуют их смыслу.

← На главную