Почему зависает 1С при формировании отчетов по взаиморасчетам из реализации в УТ 11.5 и как это исправить?

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

После обновления конфигурации «1С:Управление торговлей 11» (начиная с релизов ветки 11.5.17 и вплоть до LTS-релизов 11.5.27) пользователи нередко сталкиваются с неприятной ошибкой: при попытке сформировать отчет «Задолженность клиента» или «Ведомость расчетов» непосредственно из формы документа «Реализация товаров и услуг» сеанс 1С намертво зависает. При этом, если открыть тот же самый отчет отдельно из раздела отчетов, он формируется штатно и без задержек.

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

В чем причина избирательного зависания отчетов?

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

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

В этой процедуре формируется запрос к физической таблице движений регистра РасчетыСКлиентами и справочнику ОбъектыРасчетов. В типовом тексте запроса для порядка расчетов «По заказам / накладным» используется соединение:

ПО Расчеты.ПродажаПоЗаказу = ОбъектыРасчетов.Объект

Если в базе данных велся учет с накладными без заказов, реквизит ПродажаПоЗаказу в регистре может оставаться пустым. Кроме того, данный реквизит может иметь несоответствующий тип, из-за чего сравнение с полем ОбъектыРасчетов.Объект не может завершиться успешно по индексам.

Почему на SQL база работает, а в файловом режиме зависает?

Рассмотрим подробнее поведение СУБД при выполнении неоптимального соединения больших таблиц:

  1. Ограничения встроенной файловой СУБД (1CD): движок файловой базы не умеет строить хэш-соединения (Hash Join) и распараллеливать запросы. При соединении регистра, насчитывающего миллионы записей, с неиндексированным или пустым полем файловый движок переключается на алгоритм вложенных циклов (Nested Loops) с полным перебором (Table Scan). Если база весит более 10–14 ГБ, операция требует колоссального времени, что внешне выглядит как полное зависание платформы.
  2. Поведение клиент-серверной СУБД (PostgreSQL / MS SQL Server): внешний оптимизатор СУБД строит статистику, автоматически размещает данные во временных таблицах памяти и эффективно обрабатывает запрос за доли секунды даже при неудачном условии связи.

Пошаговое исправление запроса в коде

Для устранения проблемы выясним точное место в конфигурации. Перейдем в общий модуль ВзаиморасчетыСервер и найдем процедуру ПолучитьОбъектыРасчетовДляОтбораОтчета().

В блоке формирования текста запроса для условия Если СтруктураПараметров.ЭтоПродажаЗакупка Тогда заменим связь по реквизиту ПродажаПоЗаказу на обращение к полю Регистратор.

Посмотрим на пример исправленного фрагмента запроса:


Если СтруктураПараметров.ЭтоПродажаЗакупка Тогда
    ТекстЗапроса = ТекстЗапроса + ОбщегоНазначенияУТ.РазделительЗапросовВОбъединении() + "
    |ВЫБРАТЬ
    |    ОбъектыРасчетовДоговор.Ссылка
    |ИЗ
    |    ВтОбъектРасчетов КАК ВтОбъектРасчетов
    |        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ОбъектыРасчетов КАК ОбъектыРасчетов
    |            ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ОбъектыРасчетов КАК ОбъектыРасчетовДоговор
    |            ПО ОбъектыРасчетов.Договор = ОбъектыРасчетовДоговор.Объект
    |                И НЕ ОбъектыРасчетов.ПометкаУдаления
    |                И НЕ ОбъектыРасчетов.ТолькоОстатки
    |        ПО ВтОбъектРасчетов.ОбъектРасчетов = ОбъектыРасчетов.Ссылка
    |ГДЕ
    |    ВтОбъектРасчетов.ПорядокРасчетов = ЗНАЧЕНИЕ(Перечисление.ПорядокРасчетов.ПоДоговорамНакладным)
    |
    |ОБЪЕДИНИТЬ ВСЕ
    |
    |ВЫБРАТЬ
    |    ОбъектыРасчетов.Ссылка
    |ИЗ
    |    ВтОбъектРасчетов КАК ВтОбъектРасчетов
    |        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.РасчетыСКлиентами КАК Расчеты
    |            ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ОбъектыРасчетов КАК ОбъектыРасчетов
    |            ПО (Расчеты.Регистратор = ОбъектыРасчетов.Объект)
    |                И НЕ ОбъектыРасчетов.ПометкаУдаления
    |                И НЕ ОбъектыРасчетов.ТолькоОстатки
    |        ПО (ВтОбъектРасчетов.ОбъектРасчетов = Расчеты.ОбъектРасчетов)
    |ГДЕ
    |    ВтОбъектРасчетов.ПорядокРасчетов = ЗНАЧЕНИЕ(Перечисление.ПорядокРасчетов.ПоЗаказамНакладным)

Разберем изменения: условие Расчеты.ПродажаПоЗаказу = ОбъектыРасчетов.Объект заменено на Расчеты.Регистратор = ОбъектыРасчетов.Объект. Поскольку поле Регистратор всегда заполнено ссылкой на первичный документ и входит в кластерные индексы регистра, СУБД мгновенно сопоставляет накладную со справочником объектов расчетов без выполнения ресурсоемких сканирований всей таблицы.

Рекомендации по внедрению и сопровождению

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

  1. Используем расширение конфигурации: не следует снимать типовой модуль с поддержки. Рекомендуется подключить исправленную процедуру через расширение с директивой &ИзменениеИКонтроля или перехватом &Вместо.
  2. Контролируем переход на онлайн-взаиморасчеты: в релизах 11.5.27 офлайн-расчеты признаны устаревшими. Обязательно выполним регламентные задания распределения взаиморасчетов и закроем все прошлые месяцы во избежание накопления зависших очередей в регистрах ЗаданияКРасчетуСКлиентами.
  3. Планируем переход на клиент-сервер: при размере базы свыше 10 ГБ и интенсивном документообороте файловый режим 1С исчерпывает свои возможности. Перевод системы на SQL исключит подобные зависания в будущем на системном уровне.
← На главную