Почему в результатах запроса 1С появляется неожиданная дата 28.12.2024 и как это исправить?

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

При работе с запросами в системе 1С:Предприятие программисты часто сталкиваются с ситуацией, когда результат выборки кажется случайным или нелогичным. Одной из классических проблем является работа с производственным календарем, когда при попытке получить «следующие N рабочих дней» в выборку внезапно попадает дата из конца года (например, 28 декабря 2024 года), хотя исходная дата была установлена на середину лета.

В этой статье мы подробно разберем, почему СУБД возвращает данные именно в таком порядке, как работает функция АВТОНОМЕРЗАПИСИ и как правильно составить запрос, чтобы результат был детерминированным и корректным.

Анализ исходной проблемы

Рассмотрим ситуацию на конкретном примере. Программист пытается получить 13 рабочих дней, начиная с определенной даты, используя временную таблицу и нумерацию строк. Код исходного запроса выглядит следующим образом:


ВЫБРАТЬ ПЕРВЫЕ 13
    ДанныеПроизводственногоКалендаря.Дата КАК ДатаКалендаря,
    АВТОНОМЕРЗАПИСИ() КАК Номер
ПОМЕСТИТЬ ВТ_РабочиеДни
ИЗ
    РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеПроизводственногоКалендаря
ГДЕ
    ДанныеПроизводственногоКалендаря.ВидДня = &ВидДня_РабочийДень
    И ДанныеПроизводственногоКалендаря.Дата >= &ИсходнаяДата;

При выполнении этого кода, если параметр &ИсходнаяДата равен 15.07.2024, в таблицу ВТ_РабочиеДни под номером 1 внезапно попадает 28.12.2024. Это ломает всю дальнейшую логику расчетов. Проанализируем, почему это происходит.

Причина №1: Отсутствие явной сортировки в первом запросе

Самая важная деталь, которую необходимо запомнить: в языке SQL (и в запросах 1С) порядок строк в результате не гарантирован, если не указано предложение УПОРЯДОЧИТЬ ПО.

Когда мы пишем ВЫБРАТЬ ПЕРВЫЕ 13 без сортировки, мы фактически говорим системе: «Дай мне любые 13 записей, которые первыми попадутся тебе под руку при сканировании таблицы». СУБД (будь то MS SQL Server, PostgreSQL или любая другая) возвращает данные в том порядке, в котором они физически расположены в файлах базы данных или в соответствии с выбранным индексом.

Рассмотрим подробнее, почему именно 28 декабря становится «первым». В 2024 году 28 декабря — это рабочая суббота (перенос выходного дня). В типовых конфигурациях 1С при заполнении производственного календаря записи о переносах или ручных корректировках часто записываются в таблицу базы данных отдельно от основного массива дат. В итоге физический адрес этой записи в таблице может оказаться в самом начале или в месте, которое индекс сканирует первым. Без команды УПОРЯДОЧИТЬ ПО планировщик запросов SQL выбирает наиболее «дешевый» путь получения 13 строк, и дата 28.12.2024 просто оказывается в их числе.

Причина №2: Механизм работы АВТОНОМЕРЗАПИСИ

Функция АВТОНОМЕРЗАПИСИ в 1С тесно связана с тем порядком, в котором записи поступают из СУБД. Она присваивает порядковый номер строке в момент её формирования. Если в запросе нет сортировки, то номер 1 получит та строка, которая первой пришла из базы данных.

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

Правильное решение проблемы

Для того чтобы запрос всегда возвращал хронологически верные данные, необходимо добавить явное упорядочивание в тот момент, когда мы формируем выборку ПЕРВЫЕ 13. Исправим наш запрос, добавив УПОРЯДОЧИТЬ ПО непосредственно перед помещением во временную таблицу.

Вариант 1: Использование УПОРЯДОЧИТЬ ПО во временной таблице

Разберем по шагам правильную структуру:


ВЫБРАТЬ ПЕРВЫЕ 13
    ДанныеПроизводственногоКалендаря.Дата КАК ДатаКалендаря
ПОМЕСТИТЬ ВТ_РабочиеДни
ИЗ
    РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеПроизводственногоКалендаря
ГДЕ
    ДанныеПроизводственногоКалендаря.ВидДня = &ВидДня_РабочийДень
    И ДанныеПроизводственногоКалендаря.Дата >= &ИсходнаяДата
УПОРЯДОЧИТЬ ПО
    ДанныеПроизводственногоКалендаря.Дата; // Обязательная сортировка

В этом случае платформа 1С сначала отсортирует все рабочие дни по возрастанию даты, начиная от &ИсходнаяДата, и только потом возьмет первые 13. Теперь 28.12.2024 окажется в конце списка (если он вообще туда попадет), а не в начале.

Вариант 2: Оптимальный подход через создание периода

Как отмечают эксперты, выборка из регистра сведений может быть не самой оптимальной, если данных очень много. Рассмотрим альтернативный метод: создание временной таблицы с «запасом» дней в коде 1С и последующее соединение с календарем.

Проанализируем ситуацию: нам нужно 13 рабочих дней. Максимальное количество подряд идущих выходных (например, в январе) обычно не превышает 10. Значит, если мы возьмем период в 30 календарных дней, мы гарантированно найдем в нем 13 рабочих.

  1. Формируем в коде массив из 30 дат, начиная от исходной.
  2. Передаем этот массив в запрос.
  3. Соединяем массив с регистром ДанныеПроизводственногоКалендаря.
  4. Отбираем только рабочие дни, сортируем и берем ПЕРВЫЕ 13.

Такой подход работает быстрее на больших базах, так как СУБД использует индекс по конкретному списку дат, а не сканирует диапазон «от даты и до бесконечности».

Резюме и рекомендации

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

Посмотрим на финальный пример кода, который является наиболее устойчивым и правильным для решения задачи получения 13 рабочих дней:


ВЫБРАТЬ ПЕРВЫЕ 13
    Календарь.Дата КАК ДатаРабочая
ИЗ
    РегистрСведений.ДанныеПроизводственногоКалендаря КАК Календарь
ГДЕ
    Календарь.Дата >= &ИсходнаяДата
    И Календарь.ВидДня = ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Рабочий)
    // Если используете конкретный календарь, добавьте условие по нему
    И Календарь.ПроизводственныйКалендарь = &Календарь
УПОРЯДОЧИТЬ ПО
    ДатаРабочая ВОЗР;

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

← На главную