При выполнении запроса в платформе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда функция РАЗНОСТЬДАТ с квантом измерения СЕКУНДА приводит к критической ошибке СУБД. Текст ошибки обычно содержит следующее сообщение от Microsoft SQL Server: «Функция datediff вызвала переполнение. Слишком большое количество частей даты, разделяющих два экземпляра даты-времени». Проанализируем причину возникновения этого сбоя и рассмотрим эффективные варианты решения задачи.
Разберем технические особенности выполнения запроса на стороне СУБД. Когда мы вызываем функцию РАЗНОСТЬДАТ(Дата1, Дата2, СЕКУНДА), платформа 1С транслирует её в функцию T-SQL DATEDIFF(second, Дата1, Дата2) на уровне Microsoft SQL Server.
Результатом работы функции DATEDIFF в T-SQL всегда является 32-битное целое число со знаком (INT). Максимальное значение, которое может вместить данный тип данных, составляет 2 147 483 647.
Переведем это значение в более привычные единицы времени:
Если одно из сравниваемых полей в регистре сведений не заполнено, платформа 1С воспринимает незаполненную дату как «начало времен» — 0001-01-01 00:00:00. При попытке вычесть пустую дату из текущей даты разность составляет более 2000 лет. Это значение кардинально превышает лимит в 68 лет, что и приводит к мгновенному переполнению типом INT на стороне СУБД.
Посмотрим на самый простой и эффективный способ исправления ситуации, когда требуется получить результат в минутах. В исходном коде часто применяют вычисление разницы в секундах с последующим делением на 60:
РАЗНОСТЬДАТ(Регистрация.МоментСнятия, Регистрация.МоментЗачисления, СЕКУНДА) / 60
Вместо этого укажем квант измерения МИНУТА непосредственно в функции РАЗНОСТЬДАТ. Запас значений для минут при формате INT составляет более 4000 лет, что полностью исключает риск переполнения даже при наличии дат из первого тысячелетия.
РАЗНОСТЬДАТ(Регистрация.МоментСнятия, Регистрация.МоментЗачисления, МИНУТА) КАК РазницаМинут
Проанализируем ситуацию, когда дата снятия или зачисления может быть не заполнена либо содержит значение NULL (например, при левом соединении таблиц). Для исключения пустых значений подставим вместо отсутствующей даты вторую сравниваемую дату с помощью функции ЕСТЬNULL. В результате их разница станет равной 0, и переполнения не произойдет.
РАЗНОСТЬДАТ(ЕСТЬNULL(Регистрация.МоментСнятия, Регистрация.МоментЗачисления), Регистрация.МоментЗачисления, МИНУТА) КАК РазницаМинут
Если необходимо проверить реквизит на значение пустой даты ДАТАВРЕМЯ(1, 1, 1), используем конструкцию ВЫБОР КОГДА:
ВЫБОР
КОГДА Регистрация.МоментСнятия = ДАТАВРЕМЯ(1, 1, 1) ИЛИ Регистрация.МоментСнятия ЕСТЬ NULL
ТОГДА 0
ИНАЧЕ РАЗНОСТЬДАТ(Регистрация.МоментСнятия, Регистрация.МоментЗачисления, МИНУТА)
КОНЕЦ КАК РазницаМинут
Важная деталь: Оптимизатор MS SQL Server в редких случаях может вычислять выражение DATEDIFF для всех строк таблицы до применения условий CASE. Чтобы гарантированно исключить некорректные записи, рекомендуется отсекать их в секции ГДЕ или в параметрах виртуальной таблицы.
ГДЕ
Регистрация.МоментСнятия <> ДАТАВРЕМЯ(1, 1, 1)
И Регистрация.МоментСнятия ЕСТЬ НЕ NULL
Если для бизнес-логики принципиально получить точную разность именно в секундах для интервалов, потенциально превышающих 68 лет, разобьем вычисление на дни и оставшиеся секунды внутри суток:
РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ) * 86400
+ РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(Дата2, ДЕНЬ), Дата2, СЕКУНДА)
- РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(Дата1, ДЕНЬ), Дата1, СЕКУНДА)
В такой конструкции количество дней переводится в секунды умножением на 86400, а функция РАЗНОСТЬДАТ(..., СЕКУНДА) рассчитывается только в рамках одних суток, что никогда не приведет к выходу за пределы допустимого диапазона.
Разберем еще один подход: вынос вычислений за пределы СУБД. Встроенный язык 1С не имеет ограничения в 68 лет при работе с типами дат и интервалами.
Использование описанных методик позволяет полностью избежать ошибок переполнения СУБД и обеспечить стабильную работу отчетов и обработок.