Почему задваиваются остатки по регистру накопления в 1С и как это исправить?

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

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

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

Суть проблемы: как СУБД хранит остатки и почему они «бьются»

Чтобы понять первопричину задвоения остатков, нам необходимо заглянуть под капот платформы 1С и проанализировать, как устроена физическая структура хранения регистров накопления в СУБД (например, в MS SQL Server или PostgreSQL). Для любого регистра накопления остатков платформа создает в базе данных как минимум две ключевые таблицы:

  1. Таблица движений (хранит детальные записи, созданные документами-регистраторами). Каждая строка здесь привязана к конкретному документу и моменту времени.
  2. Таблица итогов (хранит рассчитанные агрегированные остатки в разрезе измерений на начало каждого месяца, а также наиболее актуальные текущие остатки).

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

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

Выделим основные причины рассинхронизации итогов:

Разбор полетов: почему условие в «ГДЕ» усугубляет проблему?

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


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

Этот запрос содержит грубую методологическую ошибку, которая не только критически снижает производительность системы, но и напрямую влияет на проявление проблемы «битых итогов». Разберем, что происходит на уровне СУБД при выполнении такого кода:

Когда условие накладывается в секции ГДЕ, платформа 1С сначала полностью рассчитывает остатки абсолютно по всей таблице итогов регистра на указанную дату (выбирая данные по миллионам других документов), и только после этого СУБД выполняет фильтрацию по значению ДокументРасчетов = &ВыбДокумент. Если в таблице итогов присутствует мусор по другим записям, математические погрешности рассогласования могут исказить конечную выборку.

Для исправления этой ошибки и ускорения запроса в десятки раз необходимо перенести условие отбора непосредственно в параметры виртуальной таблицы:


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

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

Как исправить ошибку с помощью Тестирования и Исправления (ТИИ)

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

Стандартный и наиболее надежный способ для файловых и небольших клиент-серверных баз данных — использование встроенного механизма «Тестирования и исправления». Рассмотрим порядок действий по шагам:

  1. Обязательно создайте резервную копию базы данных перед выполнением любых административных процедур.
  2. Запустите информационную базу в режиме Конфигуратора монопольно (все пользователи должны выйти из системы).
  3. В главном меню выберите пункт Администрирование — Тестирование и исправление...
  4. В открывшемся окне установите флаг напротив пункта Пересчет итогов.
  5. Дополнительно рекомендуется отметить пункт Реиндексация таблиц информационной базы, так как разрушение индексов часто сопутствует ошибкам в итогах.
  6. Нажмите кнопку Выполнить и дождитесь завершения процесса.

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

Альтернативные экспресс-методы исправления итогов без ТИИ

Процедура ТИИ в Конфигураторе требует монопольного доступа и на крупных базах данных (объемом в сотни гигабайт или терабайты) может выполняться часами или даже сутками. Бизнес не всегда может позволить себе такой простой системы. Рассмотрим альтернативные, более быстрые методы точечного пересчета итогов для конкретного регистра.

Способ 1. Использование обработки «Управление итогами» в режиме Предприятия

В современных конфигурациях 1С (на базе БСП) или непосредственно в самой платформе есть возможность управлять итогами без входа в Конфигуратор:

  1. Перейдите в раздел Администрирование — Поддержка и обслуживание — Регламентные операции.
  2. Откройте ссылку Управление итогами и агрегатами (в некоторых конфигурациях этот функционал доступен через функции для технического специалиста / меню «Все функции» — Стандартные — Управление итогами).
  3. Найдите в списке ваш проблемный регистр (в нашем примере это АР_ВзаиморасчетыПоСчетамНаАренду).
  4. Выберите команду Пересчитать итоги. Система выполнит пересчет только для этого регистра, не блокируя работу с другими объектами базы данных.

Способ 2. Программный сброс и расчет итогов (самый быстрый способ)

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


РегистрИмя = РегистрыНакопления.АР_ВзаиморасчетыПоСчетамНаАренду;

// Шаг 1. Отключаем использование итогов по регистру
РегистрИмя.УстановитьИспользованиеИтогов(Ложь);

// Шаг 2. Включаем использование итогов обратно
РегистрИмя.УстановитьИспользованиеИтогов(Истина);

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

Этот метод работает в разы быстрее стандартного пересчета, поскольку исключает выполнение сложных проверок транзакций и накладных расходов СУБД на обновление старых записей.

Способ 3. Программный пересчет за определенный период

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


НачалоПериода = '20180601000000'; // 1 июня 2018 года
КонецПериода  = '20180630235959'; // 30 июня 2018 года

РегистрыНакопления.АР_ВзаиморасчетыПоСчетамНаАренду.ПересчитатьИтогиЗаПериод(НачалоПериода, КонецПериода);

Этот метод позволяет минимизировать время блокировки таблиц, обновляя итоги только за указанный диапазон дат.

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

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

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

← На главную