Как правильно фильтровать по дате в SQL-запросах к базе 1С и безопасно удалять ошибочные движения регистров?

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

При прямой работе с базой данных 1С на уровне СУБД MS SQL Server разработчики нередко сталкиваются с ситуацией, когда простейший запрос с условием отбора по дате завершается ошибкой: «Сообщение 242, Преобразование типа данных varchar в тип данных datetime привело к выходу значения за пределы диапазона». Проанализируем, почему возникает эта ошибка при передаче строкового литерала даты, как правильно формировать условия по периоду и почему прямое удаление строк из таблиц движений регистров накопления через SQL приводит к повреждению целостности базы.

Причина ошибки преобразования даты в MS SQL Server

Рассмотрим механизм интерпретации строковых литералов в T-SQL. Когда мы пишем условие вида dbo._AccumRg10048._Period > '2016-02-21 09:00:02.000', СУБД пытается неявно привести строку к типу datetime.

Однако строковый формат с дефисами (YYYY-MM-DD) в MS SQL Server не является универсальным для типа datetime: его парсинг зависит от региональных настроек сессии, определяемых параметрами SET LANGUAGE и SET DATEFORMAT. Если в сеансе действует порядок даты mdy (месяц-день-год) или ydm (год-день-месяц), число 21 начинает восприниматься сервером как номер месяца, что и приводит к выходу за допустимый диапазон значений.

Кроме того, в базах 1С на уровне параметров создания информационной базы может быть задано смещение дат на 2000 лет. В таком случае даты 2016 года физически записываются в таблицы как 4016 год, что также необходимо учитывать при анализе сырых данных.

Способ 1. Использование универсальных форматов дат в T-SQL

Разберем по шагам, как правильно составить запрос к таблице 1С в среде SQL Server Management Studio, чтобы избежать ошибок региональных стандартов.

  1. Стандарт ISO 8601 с разделителем «T». Такой формат воспринимается сервером однозначно вне зависимости от настроек сессии:
    
    SELECT * 
    FROM dbo._AccumRg10048 
    WHERE _Period > '2016-02-21T09:00:02.000';
    
  2. Неразрывный формат без дефисов (YYYYMMDD). Для части с датой сервер гарантированно применяет порядок «год-месяц-день»:
    
    SELECT * 
    FROM dbo._AccumRg10048 
    WHERE _Period > '20160221 09:00:02';
    
  3. Явное приведение с указанием стиля через CONVERT. Стиль 120 жестко задает стандарт ODBC canonical (yyyy-mm-dd hh:mi:ss):
    
    SELECT * 
    FROM dbo._AccumRg10048 
    WHERE _Period > CONVERT(datetime, '2016-02-21 09:00:02', 120);
    

Опасность прямого удаления записей через SQL

Выясним причину, по которой прямое удаление строк оператором DELETE из таблицы _AccumRg... категорически не рекомендуется. В платформе 1С регистр накопления физически состоит не из одной таблицы, а как минимум из двух:

  1. Таблица движений (например, _AccumRg10048), где хранятся сами записи регистратора.
  2. Таблицы итогов и агрегатов (_AccumRgT..., _AccumRgOpt...), где платформа хранит рассчитанные остатки и обороты по периодам.

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

Способ 2. Корректное удаление движений через встроенный язык 1С

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

Посмотрим на пример кода очистки набора записей регистра:


НаборЗаписей = РегистрыНакопления.СебестоимостьТоваров.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);

// Включаем режим обмена данными для обхода обработчиков и проверок
НаборЗаписей.ОбменДанными.Загрузка = Истина;

// Записываем пустой набор: движения удалятся, а итоги пересчитаются платформой
НаборЗаписей.Записать();

Если требуется снять документ с проведения целиком в обход блокировок и проверок, воспользуемся аналогичным приемом через объект документа:


ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
ДокументОбъект.ОбменДанными.Загрузка = Истина;
ДокументОбъект.Проведен = Ложь;
ДокументОбъект.Записать();

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

Что делать, если удаление через SQL уже выполнено?

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

  1. В режиме Конфигуратора перейдем в меню Администрирование -> Тестирование и исправление, отметим пункт Пересчет итогов и запустим обработку.
  2. Либо выполним пересчет программно для конкретного регистра: РегистрыНакопления.СебестоимостьТоваров.ПересчитатьИтоги();
← На главную