Почему запрос в 1С выдает разные результаты в консоли и в коде и как правильно найти контрагентов без оборотов за период

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

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

Поиск причины расхождения: Консоль vs Программный код

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

Рассмотрим важный нюанс работы с NULL при соединениях. Когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ, а затем фильтруем результат через ЕСТЬ NULL, крайне важно, чтобы условия в секции ГДЕ не «отсекали» эти самые NULL. Разберем по шагам: если вы обращаетесь к реквизитам через точку от поля, которое может быть NULL (например, КонтрагентыСОборотамиЗаПоследнийГод.Контрагент.Наименование), результат всего условия может стать ложным, и строки пропадут из выборки. Однако в данном случае проблема оказалась прозаичнее — разница в значениях границ периодов. Всегда проверяйте типы и значения переменных перед установкой параметров запроса.

Оптимизация запроса: использование виртуальных таблиц

Многие разработчики на этапе обучения используют физические таблицы регистров (например, РегистрНакопления.ВзаиморасчетыСКонтрагентами). Посмотрим на ситуацию с точки зрения производительности. Обращение к физической таблице заставляет СУБД сканировать огромное количество записей, особенно если в базе миллионы строк за много лет. Рассмотрим более правильный подход с использованием виртуальных таблиц .Обороты.

Виртуальные таблицы в 1С опираются на предварительно рассчитанные итоги. Это позволяет системе не пересчитывать каждое движение, а брать готовые агрегаты. Проанализируем пример кода, который будет работать значительно быстрее на больших объемах данных:


ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ВзаиморасчетыОбороты.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_СДвижениямиВообще
ИЗ
    РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты(, , , ) КАК ВзаиморасчетыОбороты
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ВзаиморасчетыОбороты.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_СДвижениямиЗаГод
ИЗ
    РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты(&НачалоГода, &КонецГода, , ) КАК ВзаиморасчетыОбороты
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_СДвижениямиВообще.Контрагент
ИЗ
    ВТ_СДвижениямиВообще КАК ВТ_СДвижениямиВообще
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СДвижениямиЗаГод КАК ВТ_СДвижениямиЗаГод
        ПО ВТ_СДвижениямиВообще.Контрагент = ВТ_СДвижениямиЗаГод.Контрагент
ГДЕ
    ВТ_СДвижениямиЗаГод.Контрагент ЕСТЬ NULL

В этом примере мы используем механизм временных таблиц. Это позволяет СУБД построить оптимальный план запроса. Обратите внимание на использование параметров в самих виртуальных таблицах — это критически важно для производительности.

Альтернативный метод: группировка и условие на МАКСИМУМ

Рассмотрим ситуацию, когда нам не хочется плодить временные таблицы и сложные соединения. Существует элегантное решение задачи «вычитания» через агрегатные функции. Мы можем один раз обратиться к таблице оборотов и сгруппировать данные по контрагенту, вычислив наличие движений в нужном периоде с помощью конструкции ВЫБОР.

Разберем этот пример кода:


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

В данном случае мы получаем всех контрагентов, у которых вообще были движения, и для каждого «помечаем», попадали ли его движения в последний год. Если МАКСИМУМ от выбора вернул NULL, значит, движений в указанном интервале не было. Этот метод часто оказывается быстрее ЛЕВОГО СОЕДИНЕНИЯ, так как требует всего одного прохода по данным.

Самый эффективный способ: поиск даты последней активности

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

Рассмотрим, как это реализовать максимально эффективно:

  1. Обращаемся к виртуальной таблице .Обороты с периодичностью День.
  2. Группируем по полю Контрагент.
  3. Выбираем МАКСИМУМ(Период).
  4. Накладываем условие в секции ГДЕ на полученную дату.

Пример реализации:


ВЫБРАТЬ
    Обороты.Контрагент КАК Контрагент,
    МАКСИМУМ(Обороты.Период) КАК ДатаПоследнейАктивности
ИЗ
    РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты(, , День, ) КАК Обороты
СГРУППИРОВАТЬ ПО
    Обороты.Контрагент
ИМЕЮЩИЕ
    МАКСИМУМ(Обороты.Период) < &ДатаГраницыГода

Важный момент: использование секции ИМЕЮЩИЕ позволяет отфильтровать результат группировки по агрегатному значению (максимальной дате). Этот подход является наиболее «человекочитаемым» и производительным, так как он напрямую отвечает на вопрос: «Кто не покупал у нас больше года?».

Типичные ошибки при работе с запросами в 1С

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

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

← На главную