Функция РАЗНОСТЬДАТ является одним из наиболее часто используемых инструментов в языке запросов 1С для работы с временными интервалами. Однако новички часто сталкиваются с тем, что функция либо выдает ошибку синтаксиса, либо возвращает результат, который кажется логически неверным. В этой статье мы подробно разберем, как работает эта функция, почему "год" не всегда равен 365 дням в понимании 1С, и как правильно писать условия в запросах.
Проанализируем типичную ошибку, с которой столкнулся автор темы. В его запросе функция выглядела так: РАЗНОСТЬДАТ(ДАТАВРЕМЯ(ТекущаяДата),(ДАТАВРЕМЯ(ВТ.Период),ГОД). Здесь допущено сразу несколько критических промахов:
ДАТАВРЕМЯ(2023, 01, 01). В нее нельзя передавать переменные или имена полей.&.Рассмотрим исправленный, синтаксически верный вариант вызова функции в секции ГДЕ:
ГДЕ
РАЗНОСТЬДАТ(ВТ.Период, &ТекущаяДата, ГОД) >= 1
Здесь ВТ.Период — это начальная дата, &ТекущаяДата — конечная дата, а ГОД — единица измерения. Результат функции вычисляется как (Второй Параметр - Первый Параметр).
Выясним причину, по которой пользователь в обсуждении получил странный результат: при разнице между 28.12.2018 и 01.07.2019 функция вернула 1, хотя прошло всего полгода. Это происходит из-за специфики работы движка базы данных.
Важный момент: При расчете разности функция игнорирует все единицы измерения, которые меньше указанной. Если вы указали ГОД, система просто вычитает номер года из номера года (2019 - 2018 = 1). Состояние месяцев, дней и часов при этом вообще не учитывается. Таким образом, разница между 31 декабря и 1 января следующего года в единицах ГОД всегда будет равна 1, хотя по факту прошла всего секунда.
Аналогично это работает и для других периодов:
МЕСЯЦ: разница между 31 января и 1 февраля будет равна 1.ДЕНЬ: разница между 23:59 одного дня и 00:01 следующего будет равна 1.Если нам нужно проверить, прошел ли действительно календарный год (т.е. 365 дней), использование параметра ГОД нам не подходит. Рассмотрим более надежные способы решения этой задачи.
Способ 1: Расчет по дням
Вместо проверки разницы лет, проверим количество прошедших дней. Это самый простой и эффективный способ для большинства бизнес-задач.
ГДЕ
РАЗНОСТЬДАТ(ВТ.Период, &ТекущаяДата, ДЕНЬ) >= 365
Способ 2: Использование функции ДОБАВИТЬКДАТЕ
Если нам критически важно учитывать високосные года (где 366 дней), лучше использовать сравнение через добавление периода. Мы прибавляем к начальной дате один год и смотрим, наступил ли этот момент относительно текущей даты.
ГДЕ
&ТекущаяДата >= ДОБАВИТЬКДАТЕ(ВТ.Период, ГОД, 1)
Этот метод считается наиболее точным с точки зрения логики календаря, так как система 1С сама корректно обработает 29 февраля и прочие нюансы.
Проанализируем ситуацию, когда данные в таблицу ВТ попадают через ЛЕВОЕ СОЕДИНЕНИЕ. В этом случае поле Период может содержать значение NULL, если для остатков товара не нашлось соответствующих движений.
Помните: Если хотя бы один параметр в функции РАЗНОСТЬДАТ равен NULL, вся функция вернет NULL. В условии ГДЕ это будет интерпретировано как "Ложь", и такая запись пропадет из отчета. Чтобы избежать потери данных, рекомендуется использовать функцию ЕСТЬNULL:
РАЗНОСТЬДАТ(ЕСТЬNULL(ВТ.Период, ДАТАВРЕМЯ(1, 1, 1)), &ТекущаяДата, ДЕНЬ)
Рассмотрим важный аспект, который часто упускают программисты. Использование функций (таких как РАЗНОСТЬДАТ или ДОБАВИТЬКДАТЕ) непосредственно над полями таблиц в секции ГДЕ может значительно замедлить работу запроса на больших объемах данных. Это происходит потому, что СУБД не может использовать индекс по полю Период и вынуждена выполнять полное сканирование таблицы (Full Scan).
Для оптимизации рекомендуется заранее рассчитать граничную дату в коде 1С и передать ее в запрос как параметр:
// В коде 1С (на языке 1С)
ДатаГраница = ДобавитьМесяц(ТекущаяДата(), -12); // Отнимаем 12 месяцев
// В тексте запроса
Запрос.Текст = "ВЫБРАТЬ ... ГДЕ ВТ.Период <= &ДатаГраница";
В таком случае база данных мгновенно найдет нужные записи, используя индекс.
В сообщении №15 затронута проблема использования функции в СКД при работе с дополнительными реквизитами. Посмотрим на пример: РазностьДат([Физическое лицо.срок пропуска], [Параметры.Дата], "ДЕНЬ").
При работе в СКД нужно учитывать следующие моменты:
РАЗНОСТЬДАТ убедитесь, что реквизит приведен к типу Дата в наборе данных запроса.&Параметр. Если вы используете стандартный период, обращайтесь к &Период.ДатаНачала или &Период.ДатаОкончания.Таким образом, для корректной работы функции РАЗНОСТЬДАТ всегда четко определяйте цель: нужно ли вам просто вычесть номера лет/месяцев или же получить точную разницу в днях. Правильный выбор единицы измерения и предварительная подготовка параметров — залог быстрого и точного отчета.