При переводе старых конфигураций 1С (например, разработанных на обычных формах или использующих устаревшие режимы совместимости) на современную платформу 1С:Предприятие 8.3 разработчики часто сталкиваются с крайне загадочным поведением запросов. Типичный сценарий: простой и годами отлаженный запрос к виртуальной таблице ОстаткиИОбороты с периодичностью ДЕНЬ начинает возвращать пустые даты вида 01.01.0001 в поле периода, либо выдает результаты, кардинально отличающиеся от того, что система выдавала на платформе 8.2.
Особенно сильно запутывает то, что на тестовой базе (где сервер СУБД и сервер приложений установлены на одном компьютере) все может работать идеально, но как только конфигурация переносится в рабочую многосерверную среду, отчеты и обработки начинают выводить пустые строки или ломать логику расчетов. В этой статье мы подробно разберем, почему возникает этот сбой, проанализируем физику взаимодействия платформы с Microsoft SQL Server и рассмотрим пошаговый алгоритм полного устранения проблемы.
Давайте проанализируем типичные проявления этой ошибки на уровне разработки и администрирования:
ОстаткиИОбороты или Обороты с использованием периодичности ДЕНЬ, МЕСЯЦ или ГОД, возвращаемое поле периода (например, ПериодДень) содержит пустое значение даты — 01.01.0001.ЧАС, РЕГИСТРАТОР или ЗАПИСЬ, проблема мгновенно исчезает, и даты начинают выбираться корректно.Рассмотрим классический пример запроса, который подвержен этой уязвимости:
ВЫБРАТЬ
ПродажиОбороты.ПериодДень КАК ПериодДень,
ПродажиОбороты.Номенклатура КАК Номенклатура,
ПродажиОбороты.КоличествоОборот КАК КоличествоОборот
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК ПродажиОбороты
На проблемном сервере колонка ПериодДень в результате выполнения этого пакета будет полностью незаполнена, хотя сами движения и аналитика по номенклатуре выберутся верно.
Для того чтобы понять природу этой ошибки, нам необходимо заглянуть «под капот» взаимодействия сервера приложений 1С и СУБД MS SQL Server. Выясним, какие изменения происходят при переходе на платформу 8.3.
Начиная с режима совместимости 8.3.6, платформа 1С кардинально меняет подход к работе с типами данных даты и времени в SQL-запросах. Вместо стандартного и устаревшего типа данных datetime (который использовался в 8.2 и ранних версиях 8.3) платформа начинает активно использовать тип данных datetime2. Этот тип обладает более высокой точностью и расширенным диапазоном дат.
Когда мы обращаемся к сложным виртуальным таблицам (таким как ОстаткиИОбороты с периодичностью), платформа выполняет оптимизацию запроса. Она создает временные таблицы в системной базе данных SQL Server — tempdb. В эти временные таблицы помещаются промежуточные срезы данных, где даты приводятся к началу периода (например, усекаются до дня с помощью внутренних функций SQL).
Именно на стыке работы с временными таблицами tempdb возникает конфликт драйверов:
rphost) осуществляет подключение к СУБД и чтение результатов выборки.datetime2 во временных таблицах СУБД. При чтении среза данных из tempdb устаревший драйвер некорректно интерпретирует структуру даты, что приводит к ее «обнулению» до значения по умолчанию (01.01.0001) или к ошибкам масштабирования типа данных.Это объясняет, почему на тестовом сервере «все в одном» все работало: при установке MS SQL Server на компьютер автоматически устанавливается и актуальный Native Client. Процесс rphost использует его по умолчанию. На выделенном же сервере 1С администраторы часто забывают установить клиентскую часть SQL, оставляя сервер приложений без необходимых драйверов.
Для окончательного устранения этого сбоя необходимо обновить программное окружение сервера приложений 1С и правильно переинициализировать подключение к базе данных. Разберем этот процесс по шагам.
Вам необходимо зайти непосредственно на тот физический или виртуальный сервер, где установлена служба Сервер приложений 1С:Предприятие. Скачайте и установите актуальный дистрибутив клиентской части СУБД:
Важное предупреждение: простой перезапуск службы сервера 1С или СУБД не всегда решает проблему, так как менеджер кластера кэширует параметры соединения и продолжает использовать старый системный драйвер.
Для того чтобы принудить платформу использовать только что установленный Native Client, выполните следующие действия:
В процессе создания новой регистрации платформа заново просканирует систему на наличие установленных провайдеров данных СУБД, обнаружит новый Native Client и инициализирует сетевые сокеты с поддержкой datetime2.
В редких случаях в оперативной памяти SQL-сервера могут кэшироваться неверные планы запросов к tempdb. Чтобы окончательно исключить влияние старых трансляций, рекомендуется запустить среду SQL Server Management Studio (SSMS) и выполнить команду очистки процедурного кэша для вашей базы данных:
USE [ИмяВашейБазыДанных];
GO
DBCC FREEPROCCACHE;
GO
После выполнения этих шагов все запросы к виртуальным таблицам с периодичностью ДЕНЬ начнут возвращать корректные значения дат.
Если вы являетесь разработчиком и у вас нет прав на администрирование серверов или установку программного обеспечения, вы можете временно обойти эту проблему непосредственно в конфигурации. Рассмотрим несколько приемов.
Прием 1. Использование альтернативной периодичности. Если бизнес-логика отчета позволяет, измените периодичность виртуальной таблицы с ДЕНЬ на РЕГИСТРАТОР или ЗАПИСЬ. В этом случае платформа строит принципиально иные SQL-запросы без промежуточного усечения дат во временных таблицах:
// Вместо периодичности "День" используем "Регистратор"
// и в самом запросе получаем дату документа-регистратора
ВЫБРАТЬ
ПродажиОбороты.ПериодСекунда КАК Период,
НАЧАЛОПЕРИОДА(ПродажиОбороты.ПериодСекунда, ДЕНЬ) КАК ПериодДень,
ПродажиОбороты.Номенклатура,
ПродажиОбороты.КоличествоОборот
ИЗ
РегистрНакопления.Продажи.Обороты(, , Авто, ) КАК ПродажиОбороты
Прием 2. Перенос условий связи. Избегайте использования функции НАЧАЛОПЕРИОДА внутри условий связи таблиц (секция ПО). Некорректный драйвер SQL чаще всего ломает логику дат именно при операциях соединения таблиц по вычисляемым датам. Выносите предварительные расчеты дат во временные таблицы пакета запросов, а соединение делайте по уже готовым проиндексированным полям.