Как исключить движения конкретного регистратора из виртуальной таблицы остатков 1С

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

При разработке сложных отчетов или механизмов проведения документов в 1С программисты часто сталкиваются с ограничением виртуальной таблицы Остатки. Суть проблемы заключается в том, что механизм итогов платформы оптимизирован для быстрого получения текущих остатков, но он не позволяет «на лету» отфильтровать движения конкретного документа-регистратора внутри параметров самой виртуальной таблицы. Если нам нужно получить остатки так, будто одного определенного документа никогда не существовало, стандартный фильтр по регистратору в параметрах &Условие работать не будет, так как это поле недоступно в срезе итогов.

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

Причина возникновения проблемы

Для начала выясним причину, по которой обычный запрос к виртуальной таблице Остатки не подходит. Виртуальная таблица обращается к предварительно рассчитанным итогам (таблицы с суффиксом Totals в СУБД). Эти итоги хранятся в разрезе измерений и периодов (кратных месяцу), но не в разрезе регистраторов. Когда мы просим систему выдать остаток на 15-е число, она берет итоги на начало месяца и прибавляет к ним движения из физической таблицы за прошедшие 15 дней. Если мы попытаемся наложить условие Регистратор <> &ИсключаемыйДокумент, платформа не сможет применить его к итогам, хранящимся в базе, так как там информация о регистраторе уже «свернута».

Метод 1: Математическая корректировка через объединение запросов

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

Шаг 1: Получение основных остатков. Мы обращаемся к виртуальной таблице Остатки обычным способом, передавая нужную дату и фильтры по измерениям.

Шаг 2: Получение движений исключаемого регистратора. Мы делаем запрос к физической таблице регистра накопления, фильтруя данные по конкретной ссылке на документ.

Шаг 3: Инверсия знаков. Чтобы исключить влияние документа, мы должны выполнить обратную операцию:

  1. Если документ делал Приход, мы должны его вычесть из итогового остатка.
  2. Если документ делал Расход, мы должны его прибавить к итоговому остатку.

Посмотрим на пример реализации такого запроса:


ВЫБРАТЬ
    Остатки.Номенклатура КАК Номенклатура,
    Остатки.КоличествоОстаток КАК Количество
ПОМЕСТИТЬ ВТ_ИтоговыеДанные
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК Остатки

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Движения.Номенклатура,
    ВЫБОР
        КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
            ТОГДА -Движения.Количество
        ИНАЧЕ Движения.Количество
    КОНЕЦ
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
    Движения.Регистратор = &ИсключаемыйРегистратор
    И Движения.Период <= &Период
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Номенклатура,
    СУММА(ВТ.Количество) КАК КоличествоОстаток
ИЗ
    ВТ_ИтоговыеДанные КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Номенклатура

Важный нюанс: Обратите внимание на условие Движения.Период <= &Период. Мы должны корректировать остатки только в том случае, если исключаемый документ по времени находится «внутри» или «раньше» точки расчета остатков. Если документ записан будущим числом относительно &Период, он и так не попал в виртуальную таблицу Остатки, и корректировать его не нужно.

Метод 2: Работа с физической таблицей движений

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

Рассмотрим пример кода для этого (менее рекомендуемого) способа:


ВЫБРАТЬ
    Движения.Номенклатура,
    СУММА(ВЫБОР
        КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
            ТОГДА Движения.Количество
        ИНАЧЕ -Движения.Количество
    КОНЕЦ) КАК КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
    Движения.Регистратор <> &ИсключаемыйРегистратор
    И Движения.Период <= &Период
СГРУППИРОВАТЬ ПО
    Движения.Номенклатура

Почему это плохо? В СУБД для таблицы регистра накопления обычно существует индекс Период + Регистратор. Когда мы используем условие Регистратор <> ..., СУБД чаще всего не может эффективно использовать индекс и переходит к полному сканированию таблицы (Index Scan). Это создает колоссальную нагрузку на дисковую подсистему и память сервера.

Оптимизация производительности: использование индексов и кэша плана

Проанализируем технические детали, упомянутые опытными разработчиками. При выполнении первого метода (корректировка через ОБЪЕДИНИТЬ ВСЕ) важно понимать, как 1С работает с типами данных в SQL.

Поле Регистратор является составным типом. Когда вы передаете конкретную ссылку в параметр &ИсключаемыйРегистратор, 1С формирует запрос, который эффективно ищет запись в физической таблице по индексу. Это происходит очень быстро (Index Seek).

Также стоит упомянуть проблему «вымывания кэша планов запросов», которая обсуждалась в профессиональном сообществе. Если вы формируете текст запроса динамически, подставляя условия по типам документов, SQL-сервер может каждый раз генерировать новый план выполнения. Рекомендуется всегда использовать параметризацию (передачу ссылки через &Параметр), чтобы СУБД могла повторно использовать уже готовый план запроса.

Архитектурные советы

Если задача по исключению конкретных документов из остатков возникает регулярно, стоит задуматься: а правильно ли спроектирована система? Рассмотрим альтернативы:

  1. Разделение данных: Возможно, этот «особый» документ не должен делать движения по этому регистру вовсе? Или он должен двигать другой регистр?
  2. Дополнительное измерение: Если нам нужно постоянно видеть остатки «без учета корректировок» или «без учета технических операций», можно добавить в регистр измерение ТипОперации. В этом случае мы сможем наложить фильтр прямо в параметрах виртуальной таблицы Остатки, и это будет работать максимально быстро, используя стандартный механизм итогов 1С.
  3. Временное распроведение: Как упоминалось в обсуждении, это «плохое решение», так как оно нарушает целостность данных в многопользовательской среде и может привести к блокировкам или ошибкам в других отчетах.

Резюме

Подведем итоги нашего исследования. Чтобы красиво и эффективно исключить движения одного регистратора из остатков, мы используем следующую стратегию:

1. Используем временные таблицы для разделения логики получения остатков и получения корректирующих движений.
2. В первой временной таблице получаем данные из РегистрНакопления...Остатки.
3. Во второй временной таблице получаем движения по конкретному Регистратору, обязательно ограничивая выборку периодом, равным дате остатков.
4. Выполняем математическую инверсию: приход превращаем в расход (минус), расход — в приход (плюс).
5. Объединяем результаты и выполняем финальную группировку.

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

← На главную