В практике разработки на платформе 1С:Предприятие часто возникает классическая, но от этого не менее сложная задача: извлечь дату из текстового поля (например, из комментария к документу, назначения платежа или строкового свойства) непосредственно внутри запроса. Трудность заключается в том, что в языке запросов 1С отсутствует стандартная функция явного приведения типов вроде CAST(Строка КАК ДАТА) или ПОЛУЧИТЬДАТУ().
Такое ограничение обусловлено архитектурными особенностями платформы. Транслятор запросов SDBL должен обеспечивать кроссплатформенность кода на различных СУБД (MS SQL Server, PostgreSQL, IBM DB2, Oracle), у каждой из которых свои стандарты и локали преобразования строк. Тем не менее, разработчики нашли несколько обходных путей. В этой статье мы подробно разберем все способы решения этой задачи — от классических «костылей» до элегантных современных подходов.
Прежде чем перейти к решениям, выясним причину ограничения. Строка — это нетипизированный набор символов. Дата может быть записана множеством способов: "31.12.2023", "2023-12-31", "31/12/23" или даже "31 декабря 2023 года". Если бы СУБД пыталась неявно преобразовать строку в дату на уровне базы данных, это приводило бы к постоянным аварийным завершениям запроса при малейшем несовпадении формата или региональных настроек сервера приложений и СУБД.
Поэтому нам приходится использовать альтернативные методы. Рассмотрим их по шагам.
---
Если ваша задача — вывести эту дату в отчете, то самым простым, производительным и методически верным решением будет перенос логики преобразования из SQL-запроса на уровень встроенного языка 1С с помощью механизмов СКД.
Проанализируем этот алгоритм:
РеализацияТоваровУслуг.СчетНаОплатуПокупателю.Комментарий).Дата.Посмотрим на пример реализации такой функции общего модуля:
Функция ПолучитьДатуИзСтроки(Знач ИсходнаяСтрока) Экспорт
// Очищаем строку от лишних символов и ищем паттерн даты ДД.ММ.ГГГГ
// Например, если строка содержит: "Оплата по счету от 15.10.2023"
РегулярноеВыражение = Новый RegExp;
РегулярноеВыражение.Pattern = "\d{2}\.\d{2}\.\d{4}";
Если РегулярноеВыражение.Test(ИсходнаяСтрока) Тогда
РезультатМассив = РегулярноеВыражение.Execute(ИсходнаяСтрока);
СтрокаДаты = РезультатМассив[0].Value; // Получили "15.10.2023"
День = Число(Лев(СтрокаДаты, 2));
Месяц = Число(Сред(СтрокаДаты, 4, 2));
Год = Число(Прав(СтрокаДаты, 4));
Попытка
Возврат Дата(Год, Месяц, День);
Исключение
Возврат '00010101';
КонецПопытки;
КонецЕсли;
Возврат '00010101';
КонецФункции
В СКД во вкладке Вычисляемые поля выражение для нового поля будет выглядеть следующим образом:
ИмяОбщегоМодуля.ПолучитьДатуИзСтроки(КомментарийСчета)
Преимущества метода: Высокая скорость работы, простота поддержки кода, возможность разбора строк практически любого формата сложности.
---
Что делать, если использовать СКД и программную постобработку категорически нельзя (например, дата нужна внутри сложного запроса для связи с другими таблицами или для последующей группировки)? В этом случае на помощь приходит чистый SQL-метод генерации дат через временную таблицу.
Суть метода заключается в том, что мы генерируем внутри запроса виртуальный календарь за нужный диапазон лет, переводя числовые значения дат в строковый формат, а затем соединяем этот календарь с нашей физической таблицей по условию ПОДОБНО. Для изучения различных техник формирования последовательностей дат и чисел запросом подойдёт статья о построении диапазонов чисел и дат в запросах 1С.
Давайте разберем, как выглядит этот запрос:
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), ГОД, ТГоды.Год - 1), МЕСЯЦ, ТМесяцы.Месяц - 1), ДЕНЬ, ТДни.День - 1) КАК Дата,
ТДни.ДеньС + "." + ТМесяцы.МесяцС + "." + ТГоды.ГодС КАК ДатаСтрока
ПОМЕСТИТЬ ВтКалендарь
ИЗ
(ВЫБРАТЬ 1 КАК День, "01" КАК ДеньС ОБЪЕДИНИТЬ ВЫБРАТЬ 2, "02" ОБЪЕДИНИТЬ ВЫБРАТЬ 3, "03" ОБЪЕДИНИТЬ ВЫБРАТЬ 4, "04"
ОБЪЕДИНИТЬ ВЫБРАТЬ 5, "05" ОБЪЕДИНИТЬ ВЫБРАТЬ 6, "06" ОБЪЕДИНИТЬ ВЫБРАТЬ 7, "07" ОБЪЕДИНИТЬ ВЫБРАТЬ 8, "08"
ОБЪЕДИНИТЬ ВЫБРАТЬ 9, "09" ОБЪЕДИНИТЬ ВЫБРАТЬ 10, "10" ОБЪЕДИНИТЬ ВЫБРАТЬ 11, "11" ОБЪЕДИНИТЬ ВЫБРАТЬ 12, "12"
ОБЪЕДИНИТЬ ВЫБРАТЬ 13, "13" ОБЪЕДИНИТЬ ВЫБРАТЬ 14, "14" ОБЪЕДИНИТЬ ВЫБРАТЬ 15, "15" ОБЪЕДИНИТЬ ВЫБРАТЬ 16, "16"
ОБЪЕДИНИТЬ ВЫБРАТЬ 17, "17" ОБЪЕДИНИТЬ ВЫБРАТЬ 18, "18" ОБЪЕДИНИТЬ ВЫБРАТЬ 19, "19" ОБЪЕДИНИТЬ ВЫБРАТЬ 20, "20"
ОБЪЕДИНИТЬ ВЫБРАТЬ 21, "21" ОБЪЕДИНИТЬ ВЫБРАТЬ 22, "22" ОБЪЕДИНИТЬ ВЫБРАТЬ 23, "23" ОБЪЕДИНИТЬ ВЫБРАТЬ 24, "24"
ОБЪЕДИНИТЬ ВЫБРАТЬ 25, "25" ОБЪЕДИНИТЬ ВЫБРАТЬ 26, "26" ОБЪЕДИНИТЬ ВЫБРАТЬ 27, "27" ОБЪЕДИНИТЬ ВЫБРАТЬ 28, "28"
ОБЪЕДИНИТЬ ВЫБРАТЬ 29, "29" ОБЪЕДИНИТЬ ВЫБРАТЬ 30, "30" ОБЪЕДИНИТЬ ВЫБРАТЬ 31, "31") КАК ТДни
ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 1 КАК Месяц, "01" КАК МесяцС ОБЪЕДИНИТЬ ВЫБРАТЬ 2, "02" ОБЪЕДИНИТЬ ВЫБРАТЬ 3, "03"
ОБЪЕДИНИТЬ ВЫБРАТЬ 4, "04" ОБЪЕДИНИТЬ ВЫБРАТЬ 5, "05" ОБЪЕДИНИТЬ ВЫБРАТЬ 6, "06"
ОБЪЕДИНИТЬ ВЫБРАТЬ 7, "07" ОБЪЕДИНИТЬ ВЫБРАТЬ 8, "08" ОБЪЕДИНИТЬ ВЫБРАТЬ 9, "09"
ОБЪЕДИНИТЬ ВЫБРАТЬ 10, "10" ОБЪЕДИНИТЬ ВЫБРАТЬ 11, "11" ОБЪЕДИНИТЬ ВЫБРАТЬ 12, "12") КАК ТМесяцы
ПО ИСТИНА
ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 2020 КАК Год, "2020" КАК ГодС ОБЪЕДИНИТЬ ВЫБРАТЬ 2021, "2021" ОБЪЕДИНИТЬ ВЫБРАТЬ 2022, "2022"
ОБЪЕДИНИТЬ ВЫБРАТЬ 2023, "2023" ОБЪЕДИНИТЬ ВЫБРАТЬ 2024, "2024" ОБЪЕДИНИТЬ ВЫБРАТЬ 2025, "2025") КАК ТГоды
ПО ИСТИНА
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Реализация.Ссылка КАК Ссылка,
Реализация.СчетНаОплатуПокупателю.Комментарий КАК Комментарий,
ВтКалендарь.Дата КАК ПолученнаяДата
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ЛЕВОЕ СОЕДИНЕНИЕ ВтКалендарь КАК ВтКалендарь
ПО (Реализация.СчетНаОплатуПокупателю.Комментарий ПОДОБНО "%" + ВтКалендарь.ДатаСтрока + "%")
Рассмотрим подробнее, как работает этот запрос:
ПОЛНОЕ СОЕДИНЕНИЕ ... ПО ИСТИНА, мы получаем декартово произведение всех возможных комбинаций дат (всего 31 * 12 * 6 = 2232 записи).ДОБАВИТЬКДАТЕ, к начальной дате ДАТАВРЕМЯ(1,1,1) мы последовательно прибавляем сгенерированные года, месяцы и дни. Это дает нам полноценную типизированную дату. Также мы конкатенируем строковое представление даты в формате ДД.ММ.ГГГГ.ПОДОБНО "%" + ВтКалендарь.ДатаСтрока + "%" ищет вхождение любой из сгенерированных дат в строку комментария документа.Важное предупреждение: Данный метод является ресурсоемким для СУБД. Подобные неявные соединения по условию ПОДОБНО приводят к полному сканированию таблиц (Index Scan / Table Scan) и сильно снижают производительность базы данных. Используйте его только на небольших объемах данных или для разовых обработок.
---
Если строка гарантированно имеет фиксированный формат (например, выгрузка из сторонней системы передает дату в виде "ГГГГММДД" на строго определенных позициях символов), можно применить математический разбор строки в запросе без использования тяжелых декартовых произведений. Данный способ основан на поиске порядкового номера символов через сопоставление со строкой-шаблоном чисел.
Посмотрим на алгоритм разбора для формата строки "ГГГГММДД" (длина ровно 8 символов):
ПОДСТРОКА.ВЫБОР, сравнивая символ со строкой "0123456789".ДОБАВИТЬКДАТЕ.Разберем пример кода для определения числового значения одного символа:
ВЫБРАТЬ
// Вычисляем значение первой цифры года (тысячи)
ВЫБОР ПОДСТРОКА(Док.Комментарий, 1, 1)
КОГДА "0" ТОГДА 0 КОГДА "1" ТОГДА 1 КОГДА "2" ТОГДА 2 КОГДА "3" ТОГДА 3
КОГДА "4" ТОГДА 4 КОГДА "5" ТОГДА 5 КОГДА "6" ТОГДА 6 КОГДА "7" ТОГДА 7
КОГДА "8" ТОГДА 8 КОГДА "9" ТОГДА 9 ИНАЧЕ 0
КОНЕЦ * 1000 КАК ГодТысячи
ИЗ
Документ.РеализацияТоваровУслуг КАК Док
Подобным образом рассчитываются все разряды года, месяца и дня. Полученные суммы складываются, и к базовой пустой дате ДАТАВРЕМЯ(1, 1, 1) добавляется полученное количество лет, месяцев и дней. Этот способ работает мгновенно по сравнению с декартовым произведением, но требует жестко заданного формата входящей строки.
---
Если ваша конфигурация работает на платформе версии 8.3.20 и выше, то задача существенно упрощается. Начиная с этого релиза, разработчики фирмы «1С» добавили в язык запросов полноценные функции для работы со строками, которые ранее были доступны только во встроенном языке:
СтрНайти()СтрЗаменить()Лев(), Прав(), Сред()СокрЛП(), СокрЛ(), СокрП()ДлинаСтроки()Хотя прямое преобразование типов по-прежнему не поддерживается, эти строковые функции позволяют прямо в запросе отсечь весь лишний текст комментария, найти позиции разделителей (точек, дефисов) и передать «чистые» цифры для последующей сборки даты. Это исключает необходимость написания громоздких конструкций с ПОДСТРОКА.
---
Если вам не требуется выполнять отборы или соединения непосредственно в СУБД, правильным решением будет выгрузка строкового поля во временную таблицу значений и его последующая обработка штатными инструментами Библиотеки стандартных подсистем (БСП).
В БСП входит общий модуль СтроковыеФункцииКлиентСервер, в котором реализованы мощные механизмы разбора строк. Проанализируем пример кода:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Ссылка,
| Реализация.СчетНаОплатуПокупателю.Комментарий КАК Комментарий
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация";
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
РезультатЗапроса.Колонки.Добавить("НормализованнаяДата", Новый ОписаниеТипов("Дата"));
Для Каждого СтрокаТЗ Из РезультатЗапроса Цикл
// Используем функцию БСП для автоматического поиска и извлечения даты
СтрокаТЗ.НормализованнаяДата = СтроковыеФункцииКлиентСервер.СтрокаВДату(СтрокаТЗ.Комментарий);
КонецЦикла;
Функция СтрокаВДату() самостоятельно распознает большинство популярных форматов записи дат (включая разделители косой чертой, дефисы и точки) и вернет корректный тип данных Дата.
---
Мы подробно разобрали различные варианты получения даты из строки. Однако важно помнить: необходимость извлечения даты из текстовых полей — это верный признак архитектурной ошибки проектирования базы данных.
Текстовые комментарии предназначены исключительно для чтения пользователями, они не должны содержать ключевую бизнес-логику и связанные данные. Если вам регулярно требуется дата из комментария к счету или реализации, правильным решением будет:
РеализацияТоваровУслуг или СчетНаОплатуПокупателю новый реквизит с типом Дата.Это не только решит текущую проблему разбора строк раз и навсегда, но и ускорит выполнение ваших отчетов и запросов в сотни раз благодаря индексации СУБД.