Почему результаты запроса в Консоли и в Форме выбора различаются и как это исправить?

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

В практике разработки на платформе 1С 8.3 часто встречается загадочная ситуация: один и тот же запрос, выполненный в Консоли запросов, возвращает нужные данные, а в Динамическом списке на форме (например, в форме выбора) эти данные отсутствуют. Особенно остро эта проблема проявляется при работе с временными показателями, когда записи в регистрах сведений или накопления создаются «внутри» текущего дня.

Рассмотрим типичный сценарий: оборудование сменило место базирования в 7:00 утра. Мы пытаемся оформить документ выдачи этого оборудования в 8:00 того же дня. В форме выбора пусто, хотя запрос в консоли подтверждает наличие записи. Оборудование магическим образом появляется в списке только на следующие сутки. Проанализируем, почему так происходит, и найдем изящное техническое решение.

Причина проблемы: Особенности работы Динамического списка

Разберем, почему стандартный подход с использованием функций внутри запроса динамического списка может давать сбой. Динамический список — это не просто прямой запрос к базе данных, это объект, работающий на базе Системы компоновки данных (СКД). Когда мы пишем внутри текста запроса конструкцию типа ДОБАВИТЬКДАТЕ(&Период, СЕКУНДА, &Время * 3600) для виртуальной таблицы СрезПоследних, платформа может интерпретировать это некорректно при оптимизации запроса.

Основные факторы, влияющие на результат:

  1. Оптимизатор СКД: При определенных условиях СКД может выносить условия из параметров виртуальной таблицы в общий блок ГДЕ. Это критично для среза последних: вместо получения состояния на конкретный момент времени, система может сначала получить текущий срез, а потом отфильтровать его по дате, что логически неверно.
  2. Типизация параметров: Если параметр передается как выражение, платформа не всегда может точно сопоставить его с индексами таблиц базы данных.
  3. Кэширование: Динамические списки активно кэшируют данные для ускорения работы интерфейса.

Выясним причину на примере исходного (проблемного) кода запроса:


ВЫБРАТЬ
    БазировкаСрезПоследних.Оборудование,
    БазировкаСрезПоследних.АдресБазы
ИЗ
    РегистрСведений.Базировка.СрезПоследних(
        ДОБАВИТЬКДАТЕ(&Период, СЕКУНДА, &Время * 3600), 
    ) КАК БазировкаСрезПоследних

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

Решение №1: Предварительный расчет параметров в коде

Самый надежный способ решить проблему — полностью отказаться от вычислений внутри текста запроса динамического списка. Нам необходимо вычислить точную дату и время на стороне сервера 1С и передать в параметр уже готовый объект типа Дата.

Рассмотрим, как правильно модифицировать процедуру ПриСозданииНаСервере в модуле формы выбора:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Получаем исходные данные из параметров формы
    ДатаДокумента = Параметры.Период; 
    Часы = Параметры.Время; // Например, число 8
    
    // Формируем точную дату для среза последних
    // Используем НачалоДня, чтобы гарантированно уйти от лишних секунд в дате документа
    ТочныйПериод = НачалоДня(ДатаДокумента) + (Часы * 3600);
    
    // Устанавливаем готовое значение в параметр динамического списка
    Список.Параметры.УстановитьЗначениеПараметра("ПериодОтбора", ТочныйПериод);
    Список.Параметры.УстановитьЗначениеПараметра("АдресБазы", Параметры.АдресБазы);
    
КонецПроцедуры

В самом запросе динамического списка текст теперь будет выглядеть гораздо проще и стабильнее:


ВЫБРАТЬ
    БазировкаСрезПоследних.Оборудование КАК Оборудование,
    БазировкаСрезПоследних.АдресБазы КАК АдресБазы
ИЗ
    РегистрСведений.Базировка.SrezПоследних(&ПериодОтбора, ) КАК БазировкаСрезПоследних
ГДЕ
    БазировкаСрезПоследних.АдресБазы = &АдресБазы

Решение №2: Оптимизация условий исключения

Часто в формах выбора требуется исключить оборудование, которое уже занято (например, есть движения по регистру накопления). Использование виртуальных таблиц ОстаткиИОбороты внутри запроса динамического списка — плохая практика, которая сильно снижает производительность.

Проанализируем более эффективный подход: сначала получим список исключаемого оборудования отдельным запросом, а затем передадим этот список (массив ссылок) в параметр динамического списка.

Пример реализации в модуле формы:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    МояДата = Параметры.Период;
    МоеВремя = Параметры.Время;
    НужныйМомент = НачалоДня(МояДата) + МоеВремя * 3600;

    // 1. Получаем список занятого оборудования
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   РегистрАрендыОстатки.Оборудование КАК Оборудование
        |ИЗ
        |   РегистрНакопления.РегистрАренды.Остатки(&Период, ) КАК РегистрАрендыОстатки";
    
    // Для получения остатка "на сейчас" можно добавить 1 секунду
    Запрос.УстановитьПараметр("Период", НужныйМомент + 1);
    
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    МассивИсключений = РезультатЗапроса.ВыгрузитьКолонку("Оборудование");

    // 2. Настраиваем динамический список
    Список.Параметры.УстановитьЗначениеПараметра("ПериодОтбора", НужныйМомент);
    Список.Параметры.УстановитьЗначениеПараметра("ИсключаемоеОборудование", МассивИсключений);
    // Остальные параметры...

КонецПроцедуры

В тексте запроса динамического списка используем условие: ГДЕ НЕ Оборудование В (&ИсключаемоеОборудование). Это позволит избежать сложных соединений «на лету», и платформа сможет эффективно использовать индексы основной таблицы справочника.

Важные рекомендации по работе с временем в 1С

Чтобы избежать расхождений в результатах, всегда помните о следующих правилах:

  1. Объект «Граница»: Если вам нужно включить движения, сделанные ровно в ту же секунду, на которую строится отчет или срез, используйте объект Граница с видом ВидГраницы.Включая. Это особенно актуально для регистров сведений с режимом записи «В пределах секунды».
  2. Основная таблица: В динамическом списке всегда старайтесь указывать Основную таблицу. Если ваш запрос содержит ПОМЕСТИТЬ (временные таблицы), использование основной таблицы становится невозможным, что отключает стандартные механизмы поиска и сортировки 1С.
  3. Приведение к началу дня: При выполнении расчетов времени (сложение секунд) всегда делайте НачалоДня() от исходной даты. Это исключит ситуацию, когда в исходной дате документа уже были записаны какие-то секунды или миллисекунды, которые при сложении дадут непредвиденный сдвиг.
  4. Тип регистра: Если регистр сведений имеет периодичность «В пределах дня», время в параметре &Период будет игнорироваться платформой, и срез всегда будет браться на конец дня. Убедитесь, что ваш регистр имеет периодичность «В пределах секунды».

Проанализировав ситуацию, мы выяснили, что ключ к исправлению ошибки — детерминированность параметров. Когда мы передаем в динамический список готовую дату, рассчитанную на сервере, мы лишаем СКД возможности «ошибиться» при трансляции запроса в SQL, обеспечивая идентичность работы формы и консоли запросов.

← На главную