Почему в запросе 1С не срабатывает условие МЕЖДУ для даты конца дня и как это исправить

Программист 1С v8.3 (Обычные формы)
← На главную

При работе с запросами в системе 1С:Предприятие одной из самых частых задач является выборка данных за определенный период. Казалось бы, использование стандартного оператора МЕЖДУ (BETWEEN) совместно с функцией КонецДня() должно гарантировать попадание всех документов суток в результат. Однако на практике разработчики часто сталкиваются с ситуацией, когда записи, созданные в последнюю секунду дня (23:59:59), «выпадают» из отчета. В этой статье мы подробно разберем, почему это происходит, рассмотрим внутреннюю логику работы платформы и найдем наиболее надежные способы решения этой проблемы.

Разбираем логику работы оператора МЕЖДУ

Для начала проанализируем, как именно платформа 1С транслирует оператор МЕЖДУ в язык SQL, который понимает база данных (MS SQL Server, PostgreSQL или другие). Когда мы пишем в запросе условие:


ГДЕ
    ТоварыВРознице.Период МЕЖДУ &НачПериода И &КонПериода

Система преобразует его в два сравнения, объединенных логическим «И»:


ГДЕ
    (ТоварыВРознице.Период >= &НачПериода) И (ТоварыВРознице.Период <= &КонПериода)

Важно понимать, что в 1С тип данных Дата всегда содержит время с точностью до секунды. Если мы устанавливаем параметр &КонПериода с помощью функции КонецДня(Дата), то значение параметра будет равно, например, 31.12.2023 23:59:59. Теоретически, все записи с этим временем должны попасть в выборку. Но на практике возникают нюансы, связанные с типом таблиц и точностью хранения данных.

Физические таблицы против виртуальных

Рассмотрим ситуацию, описанную автором темы. Запрос выполняется к физической таблице регистра накопления РегистрНакопления.ТоварыВРознице. В таких таблицах записи хранятся «как есть». Проанализируем возможные причины ошибки:

  1. Опечатки в параметрах: Это самая тривиальная, но частая причина. Как выяснилось в ходе обсуждения, автор использовал в коде установку параметра Запрос.УстановитьПараметр("КонПериода", КонПериода), а в самом тексте запроса написал &ДКонПериода. Из-за лишней буквы «Д» параметр не передавался корректно, что и приводило к потере данных.
  2. Особенности работы с объектом Граница: Один из участников форума предложил использовать объект Новый Граница(КонецДня(Дата), ВидГраницы.Включая). Нужно четко осознавать: этот объект предназначен исключительно для параметров виртуальных таблиц (например, ОстаткиИОбороты). При использовании в обычном условии ГДЕ физической таблицы, 1С преобразует Границу в простую Дату, и весь смысл «включения» границы теряется.
  3. Миллисекунды: Хотя штатно 1С не поддерживает доли секунд в типе Дата, при интеграции с внешними системами или прямых записях в SQL теоретически могут возникнуть записи с временем чуть больше, чем 23:59:59.000. В этом случае строгое сравнение <= 23:59:59 их отсечет.

Способ 1: Использование функций языка запросов

Чтобы избежать ошибок при подготовке параметров в коде (на языке 1С), мы можем перенести логику приведения дат непосредственно в текст запроса. Это делает код более защищенным от случайных изменений переменных. Рассмотрим, как применить функции НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА внутри запроса:


Запрос.Текст = 
    "ВЫБРАТЬ
    |   Товары.Номенклатура,
    |   Товары.Количество
    |ИЗ
    |   РегистрНакопления.ТоварыВРознице КАК Товары
    |ГДЕ
    |   Товары.Период МЕЖДУ НАЧАЛОПЕРИОДА(&НачПериода, ДЕНЬ) И КОНЕЦПЕРИОДА(&КонПериода, ДЕНЬ)";

В этом случае, даже если вы передадите в параметры дату с произвольным временем (например, середину дня), запрос самостоятельно «растянет» границы до начала первой секунды и конца последней секунды указанных суток.

Способ 2: Исключение оператора МЕЖДУ в пользу строгих сравнений

Проанализируем более надежный профессиональный подход. Многие опытные разработчики предпочитают не использовать МЕЖДУ, а работать с интервалом «больше или равно началу» и «строго меньше начала следующего дня». Это гарантирует захват абсолютно всех движений, даже если система в будущем начнет поддерживать микросекунды.

Реализуется это следующим образом:


// В коде 1С
НачПериода = НачалоДня(Объект.ДатаНачала);
КонПериода = НачалоДня(Объект.ДатаОкончания) + 86400; // Добавляем сутки (60*60*24 сек)

// В тексте запроса
ГДЕ
    Регистр.Период >= &НачПериода И Регистр.Период < &КонПериода

Использование строгого знака < (меньше) вместо <= по отношению к началу следующего дня — это стандарт «чистого кода» в 1С, который исключает любые пограничные ошибки.

Способ 3: Работа с виртуальными таблицами и Границей

Если ваша задача — получить обороты или остатки из регистра, а не просто записи физической таблицы, следует использовать виртуальные таблицы. Рассмотрим пример с использованием объекта Граница, который упоминался в сообщении 2:


КонецПериодаДляЗапроса = Новый Граница(КонецДня(КонПериода), ВидГраницы.Включая);

Запрос.Текст = 
    "ВЫБРАТЬ
    |   Обороты.КоличествоОборот
    |ИЗ
    |   РегистрНакопления.ТоварыВРознице.Обороты(&НачПериода, &КонПериода, , ) КАК Обороты";

Запрос.УстановитьПараметр("НачПериода", НачалоДня(НачПериода));
Запрос.УстановитьПараметр("КонПериода", КонецПериодаДляЗапроса);

Важный момент: объект Граница позволяет системе понять, нужно ли включать документы, имеющие тот же «момент времени», что и граница. Это критично для регистров накопления, где в одну секунду может быть записано множество документов.

Типичные ошибки при написании условий

Проанализируем ситуацию из сообщения 11. Автор предоставил объемный запрос, в котором допустил классическую ошибку невнимательности. Посмотрим на фрагмент:


// В коде устанавливается один параметр:
ЗапросРасход.УстановитьПараметр("КонПериода", КонПериода);

// В тексте запроса используется другой:
|ГДЕ
| (ТоварыВРознице.Период МЕЖДУ &НачПериода И &ДКонПериода)

При выполнении такого кода запрос может либо выдать ошибку «Параметр не установлен», либо (если в менеджере временных таблиц или в другом месте кода этот параметр случайно был определен пустой датой) вернуть пустой результат или некорректную выборку. Всегда проверяйте соответствие имен параметров в тексте запроса и в методе УстановитьПараметр().

Влияние на производительность

Выясним, влияет ли выбор способа ограничения периода на скорость работы базы данных. Поле Период во всех регистрах 1С является индексированным. Когда мы используем условие Период МЕЖДУ ... или сравнения >= и <=, СУБД выполняет операцию Index Seek (поиск по индексу), что происходит очень быстро.

Однако, если вы решите применить функцию к полю таблицы, например:


// ТАК ДЕЛАТЬ НЕЛЬЗЯ (НИЗКАЯ ПРОИЗВОДИТЕЛЬНОСТЬ)
ГДЕ
    НАЧАЛОПЕРИОДА(ТоварыВРознице.Период, ДЕНЬ) = &НужныйДень

То индекс по полю Период перестанет использоваться должным образом, и система будет вынуждена перебирать все записи таблицы (Index Scan). Поэтому всегда старайтесь оставлять поле Период в левой части условия «чистым», а все преобразования (приведение к началу или концу дня) делайте с параметрами в правой части условия.

Итоги и рекомендации

Подводя итог нашему разбору, выделим основные правила работы с датами в запросах 1С:

  1. Для физических таблиц: Используйте либо КОНЕЦПЕРИОДА(&Дата, ДЕНЬ) прямо в запросе, либо конструкцию Период < &НачалоСледующегоДня.
  2. Для виртуальных таблиц: Всегда передавайте в параметры Границу с видом Включая, если нужно захватить итоги на конкретный момент времени.
  3. Внимательность: Тщательно проверяйте имена параметров. Опечатка в одну букву может стоить нескольких часов отладки.
  4. Индексация: Никогда не используйте функции над полями таблиц в блоке ГДЕ, если по этим полям построены индексы. Применяйте функции только к параметрам.

Рассмотренные методы позволяют гарантировать, что ни один документ не потеряется при формировании отчетов, а ваши запросы будут работать максимально эффективно и предсказуемо. Чтобы дополнительно проконтролировать отсутствие расхождений в проводках после исправления условий запроса или переноса данных, можно использовать сервис для автоматической сверки бухгалтерских и налоговых проводок между базами 1С.

← На главную