Почему виртуальная таблица остатков регистра накопления возвращает разные данные при изменении состава полей?

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

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

Разберем детально, как устроены механизмы платформы, почему это происходит и как правильно проектировать регистры, чтобы избегать подобных проблем.

Механизм работы виртуальной таблицы «Остатки»

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

Когда мы обращаемся к виртуальной таблице, 1С выполняет следующие действия:

  1. Определяет набор измерений: Платформа анализирует секцию ВЫБРАТЬ вашего запроса. Все измерения регистра, которые вы в ней указали, автоматически становятся полями группировки в итоговом SQL-запросе (конструкция GROUP BY).
  2. Обращается к физическим таблицам: Данные извлекаются из таблицы итогов (где хранятся рассчитанные значения на начало каждого месяца) и таблицы движений (записей регистра за период от последней точки итогов до указанной даты).
  3. Агрегирует ресурсы: Значения ресурсов (например, КоличествоОстаток) суммируются в разрезе выбранных измерений.

Рассмотрим на примере автора темы. В регистре есть три измерения: ДатаОкончание, ИзделиеКлиента и Контрагент. В базу записаны два движения:

Проанализируем ситуацию №1: в запросе выбрано поле ДатаОкончание. Платформа видит, что измерения различаются датой. СУБД получает две разные строки, так как комбинации «Вася-Ботинки-10.05» и «Вася-Ботинки-11.05» уникальны. В итоге мы видим +1 и -1.

Проанализируем ситуацию №2: поле ДатаОкончание исключено из запроса. Платформа 1С оптимизирует SQL-запрос и убирает это поле из группировки GROUP BY. Теперь для СУБД обе записи имеют одинаковый набор ключевых полей: «Вася-Ботинки». Система складывает ресурсы (+1 + (-1)) и получает 0. Это и есть стандартное поведение виртуальных таблиц: агрегация производится только по тем измерениям, которые указаны в запросе.

Использование даты в качестве измерения: почему это «антипаттерн»?

В приведенном примере ДатаОкончание является измерением регистра. Выясним причину, по которой опытные разработчики крайне не рекомендуют использовать дату (особенно со временем) в качестве измерения.

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

2. Падение производительности. При записи нового документа системе приходится пересчитывать итоги. Если измерений слишком много и они уникальны (как дата), индекс таблицы итогов становится огромным и фрагментированным. Запись документов начинает замедляться, а отчеты — «тормозить».

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

Решение: Если вам нужно хранить срок годности или дату окончания, лучше использовать их как Реквизит регистра, если по ним не требуется получать остатки в разрезе каждой даты. Если же разрез по датам критически необходим (например, для партионного учета по срокам), используйте тип Дата без времени (приведенную к началу дня) или специальный справочник.

Особенности работы через СКД (Систему компоновки данных)

Проанализируем ситуацию в контексте СКД, так как автор использовал именно её. У СКД есть механизм «Автозаполнение», который вносит свои коррективы в итоговый запрос.

В СКД запрос к виртуальной таблице может выглядеть так:


ВЫБРАТЬ
    ОстаткиИзделийОстатки.Контрагент КАК Контрагент,
    ОстаткиИзделийОстатки.ИзделиеКлиента КАК ИзделиеКлиента,
    ОстаткиИзделийОстатки.КоличествоОстаток КАК КоличествоОстаток,
    ОстаткиИзделийОстатки.ДатаОкончание КАК ДатаОкончание
ИЗ
    РегистрНакопления.ОстаткиИзделий.Остатки КАК ОстаткиИзделийОстатки

Если пользователь в настройках отчета на вкладке «Выбранные поля» уберет галочку с поля ДатаОкончание, СКД (благодаря механизму компоновки) может полностью исключить это поле из SQL-запроса к базе данных. В этот момент и происходит «схлопывание» остатков. Это не ошибка СКД, а её интеллектуальная функция оптимизации: зачем нагружать сервер группировкой по полю, которое не нужно выводить?

Разница между фильтрами: ПАРАМЕТРЫ vs ГДЕ

Выясним еще один важный технический момент, влияющий на получение остатков. Посмотрим на два варианта написания запроса.

Вариант 1 (Медленный):


ВЫБРАТЬ
    Остатки.КоличествоОстаток
ИЗ
    РегистрНакопления.Товары.Остатки() КАК Остатки
ГДЕ
    Остатки.Контрагент = &Контрагент

В этом случае система сначала получит из базы все остатки по всем контрагентам, сформирует временную таблицу, и только потом отфильтрует нужного «Васю». Это крайне неэффективно.

Вариант 2 (Правильный):


ВЫБРАТЬ
    Остатки.КоличествоОстаток
ИЗ
    РегистрНакопления.Товары.Остатки(, Контрагент = &Контрагент) КАК Остатки

Здесь фильтрация передается напрямую в параметры виртуальной таблицы. СУБД на самом низком уровне отберет только нужные записи из таблиц итогов. Скорость выполнения такого запроса будет в десятки раз выше.

Подведем итоги

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

  1. Проверяем состав измерений: Всегда помним, что виртуальная таблица Остатки суммирует ресурсы только в разрезе полей, указанных в ВЫБРАТЬ.
  2. Минимизируем измерения: Не добавляем в измерения поля, которые часто меняются (например, даты с точностью до секунды или комментарии).
  3. Используем параметры виртуальных таблиц: Всегда накладываем условия (отборы) в скобках после имени таблицы Остатки(...), а не в секции ГДЕ.
  4. Контролируем СКД: Помним, что состав полей в настройках отчета напрямую влияет на итоговый SQL-запрос и агрегацию данных.

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

← На главную