При организации интеграции между 1С и внешними базами данных на базе MSSQL программисты часто сталкиваются с ошибкой: «Преобразование типа данных varchar в тип данных datetime привело к выходу значения за пределы диапазона». Эта проблема возникает из-за различий в представлении даты внутри платформы 1С и в правилах интерпретации строк SQL-сервером.
В этой статье мы подробно разберем, почему возникает эта ошибка, как правильно форматировать дату в коде 1С и какие дополнительные настройки на стороне SQL помогут сделать обмен данными стабильным.
Прежде чем переходить к коду, выясним причину сбоя. В MSSQL стандартный тип данных DATETIME имеет ограничение: он поддерживает даты в диапазоне от 1 января 1753 года до 31 декабря 9999 года. В 1С «пустая» дата соответствует значению 01.01.0001.
Когда вы пытаетесь передать пустую дату из 1С в поле DATETIME таблицы MSSQL, сервер получает значение «0001-01-01», которое находится вне допустимого диапазона, и выдает ошибку. Также проблема может быть в том, что SQL-сервер неверно распознает порядок дня и месяца в присланной вами строке (например, ожидает месяц-день-год, а получает год-день-месяц).
Самый простой и распространенный способ — это явное приведение даты к строковому виду, который «понятен» SQL-серверу. Рассмотрим, как дополнить стандартный запрос 1С, чтобы он корректно воспринимался базой данных.
Проанализируем пример кода, где мы формируем условие WHERE для запроса к внешней таблице:
// Формируем строку условия с учетом времени
ТекстЗапроса = "SELECT * FROM table_re WHERE Date_Re >= '"
+ Формат(ДатаНачала, "ДФ=""гггг-MM-дд""") + " 00:00:00"
+ "' AND Date_Re <= '"
+ Формат(ДатаОкончания, "ДФ=""гггг-MM-дд""") + " 23:59:59" + "'";
В данном примере мы используем функцию Формат с маской гггг-MM-дд. Обратите внимание, что добавление константы времени 00:00:00 помогает SQL-серверу однозначно интерпретировать строку как тип DATETIME.
Посмотрим на более надежный метод. В зависимости от настроек локализации (языка) SQL-сервера, формат с дефисами (гггг-мм-дд) может быть воспринят неоднозначно. Существует универсальный формат YYYYMMDD, который MSSQL всегда интерпретирует как «год-месяц-день» независимо от настроек DATEFORMAT.
Реализуем этот подход в 1С:
// Универсальный формат без дефисов и точек
СтрокаДата = Формат(ДатаНачала, "ДФ=ггггММдд");
ТекстЗапроса = "INSERT INTO table1 (date_incoming) VALUES ('" + СтрокаДата + "')";
Использование ггггММдд исключает влияние региональных настроек сервера и считается «золотым стандартом» при формировании текстовых SQL-запросов вручную.
Проанализируем ситуацию, когда дата в 1С может быть не заполнена. Чтобы избежать выхода за пределы диапазона (ранее упоминавшийся 1753 год), необходимо добавить проверку перед выгрузкой.
Рассмотрим пример функции-обертки:
Функция ПодготовитьДатуДляSQL(ВхДата)
Если Не ЗначениеЗаполнено(ВхДата) Или ВхДата < '17530101' Тогда
// Возвращаем минимально допустимую дату для SQL или NULL
Возврат "17530101";
Иначе
Возврат Формат(ВхДата, "ДФ=ггггММдд");
КонецЕсли;
КонецФункции
Если структура таблицы в MSSQL позволяет записывать пустые значения, вместо «17530101» лучше возвращать строку NULL (без кавычек в тексте запроса).
Выясним, как полностью уйти от проблем форматирования. Вместо склейки строк («динамический SQL») рекомендуется использовать параметры через объект Command. В этом случае драйвер (например, SQLOLEDB или MSOLEDBSQL) сам позаботится о правильной передаче даты.
Разберем по шагам:
ADODB.Connection.ADODB.Command.adDBTimeStamp.
// Пример использования параметров
Command = Новый COMОбъект("ADODB.Command");
Command.ActiveConnection = Connection; // Ваше открытое соединение
Command.CommandText = "INSERT INTO table1 (date_field) VALUES (?)";
// Создаем параметр: имя, тип (135 = adDBTimeStamp), направление, размер, значение
Param = Command.CreateParameter("pDate", 135, 1, , ДатаИз1С);
Command.Parameters.Append(Param);
Command.Execute();
Этот способ является наиболее профессиональным, так как защищает от SQL-инъекций и автоматически решает вопросы с форматами дат и разделителями дробной части чисел.
Если вы не можете оперативно изменить программный код в 1С, ситуацию можно исправить на стороне SQL-сервера. Перед основным запросом можно выполнить инструкцию, которая укажет серверу, как трактовать входящие строки.
Рассмотрим SQL-команду:
-- Устанавливаем порядок: год, месяц, день
SET DATEFORMAT ymd;
SELECT * FROM table_re WHERE Date_Re >= '2017-09-01';
Эта команда действует в рамках текущей сессии подключения. Она сообщает серверу, что в строке '2017-09-01' первым идет год, вторым — месяц, а третьим — день.
Если вы проектируете структуру таблицы в MSSQL самостоятельно, рассмотрите возможность использования типа DATETIME2 вместо устаревшего DATETIME. Тип DATETIME2 поддерживает диапазон дат с 0001-01-01, что идеально совпадает с «пустой» датой 1С и полностью снимает проблему выхода за пределы диапазона.
Резюмируем: для быстрого исправления используйте формат ггггММдд в функции Формат(), а для надежной работы в крупных проектах переходите на параметризованные запросы через ADODB.Command.