В процессе разработки и сопровождения конфигураций на платформе 1С:Предприятие специалисты нередко сталкиваются с загадочным поведением регистров накопления. Типичная ситуация: в физической таблице движений регистра все записи выглядят корректно, документы проводятся и распроводятся без ошибок, но при попытке получить остатки через запрос или отчеты система выдает некорректные, «фантомные» или задвоенные суммы. Иногда остатки отображаются даже по тем документам, которые полностью распроведены или удалены.
В этой статье мы подробно разберем, почему возникает этот неприятный эффект, проанализируем разницу в поведении виртуальных таблиц при различных условиях в запросах, а также рассмотрим практические способы исправления поврежденных итогов регистра — от стандартного тестирования и исправления до быстрых программных методов пересчета базы данных.
Чтобы понять первопричину задвоения остатков, нам необходимо заглянуть под капот платформы 1С и проанализировать, как устроена физическая структура хранения регистров накопления в СУБД (например, в MS SQL Server или PostgreSQL). Для любого регистра накопления остатков платформа создает в базе данных как минимум две ключевые таблицы:
Когда мы обращаемся к виртуальной таблице Остатки() в запросе, платформа 1С не рассчитывает остаток с самого «начала времен» по таблице движений — это заняло бы слишком много времени на больших базах. Вместо этого система берет готовую ближайшую опорную точку из таблицы итогов и прибавляет или вычитает к ней движения за короткий промежуток времени до нужной даты. Это обеспечивает высокую скорость работы платформы.
Однако в силу различных обстоятельств между таблицей движений и таблицей итогов может возникнуть рассогласование (рассинхронизация). В профессиональной среде разработчиков это явление называют «битыми итогами». В этом случае в таблице итогов «зависает» сумма, которой на самом деле нет в таблице движений. При запросе остатков платформа берет эту ошибочную сумму за основу, что приводит к визуальному задвоению или искажению результатов.
Выделим основные причины рассинхронизации итогов:
splitter) могут некорректно суммироваться платформой.Давайте проанализируем запрос, с которого началось обсуждение проблемы на форуме. Автор пытался получить остатки по конкретному документу расчетов следующим образом:
ВЫБРАТЬ
АР_ВзаиморасчетыПоСчетамНаАрендуОстатки.СуммаОстаток КАК СуммаОстаток
ИЗ
РегистрНакопления.АР_ВзаиморасчетыПоСчетамНаАренду.Остатки(&ДатаКон, ) КАК АР_ВзаиморасчетыПоСчетамНаАрендуОстатки
ГДЕ
АР_ВзаиморасчетыПоСчетамНаАрендуОстатки.ДокументРасчетов = &ВыбДокумент
Этот запрос содержит грубую методологическую ошибку, которая не только критически снижает производительность системы, но и напрямую влияет на проявление проблемы «битых итогов». Разберем, что происходит на уровне СУБД при выполнении такого кода:
Когда условие накладывается в секции ГДЕ, платформа 1С сначала полностью рассчитывает остатки абсолютно по всей таблице итогов регистра на указанную дату (выбирая данные по миллионам других документов), и только после этого СУБД выполняет фильтрацию по значению ДокументРасчетов = &ВыбДокумент. Если в таблице итогов присутствует мусор по другим записям, математические погрешности рассогласования могут исказить конечную выборку.
Для исправления этой ошибки и ускорения запроса в десятки раз необходимо перенести условие отбора непосредственно в параметры виртуальной таблицы:
ВЫБРАТЬ
АР_ВзаиморасчетыПоСчетамНаАрендуОстатки.СуммаОстаток КАК СуммаОстаток
ИЗ
РегистрНакопления.АР_ВзаиморасчетыПоСчетамНаАренду.Остатки(&ДатаКон, ДокументРасчетов = &ВыбДокумент) КАК АР_ВзаиморасчетыПоСчетамНаАрендуОстатки
В этом случае СУБД наложит фильтр еще на этапе выборки строк из таблиц базы данных, проигнорировав лишние записи. Это снижает вероятность того, что сторонний «мусор» в итогах повлияет на расчет остатка по конкретному документу.
Если рассинхронизация уже произошла, оптимизация запросов поможет лишь частично скрыть симптомы, но не решит саму проблему повреждения базы данных. Для полного исправления ситуации требуется пересчитать итоги регистра.
Стандартный и наиболее надежный способ для файловых и небольших клиент-серверных баз данных — использование встроенного механизма «Тестирования и исправления». Рассмотрим порядок действий по шагам:
Во время пересчета итогов платформа полностью очищает физические таблицы итогов регистров и последовательно сканирует таблицы движений, заново рассчитывая и записывая правильные агрегаты на каждую контрольную точку (начало каждого месяца). После завершения процедуры все фантомные остатки исчезнут.
Процедура ТИИ в Конфигураторе требует монопольного доступа и на крупных базах данных (объемом в сотни гигабайт или терабайты) может выполняться часами или даже сутками. Бизнес не всегда может позволить себе такой простой системы. Рассмотрим альтернативные, более быстрые методы точечного пересчета итогов для конкретного регистра.
В современных конфигурациях 1С (на базе БСП) или непосредственно в самой платформе есть возможность управлять итогами без входа в Конфигуратор:
АР_ВзаиморасчетыПоСчетамНаАренду).Существует крайне эффективный программный прием, который позволяет мгновенно восстановить целостность итогов конкретного регистра. Он заключается в полном отключении использования итогов с последующим их включением. Напишем простую обработку с использованием следующего кода:
РегистрИмя = РегистрыНакопления.АР_ВзаиморасчетыПоСчетамНаАренду;
// Шаг 1. Отключаем использование итогов по регистру
РегистрИмя.УстановитьИспользованиеИтогов(Ложь);
// Шаг 2. Включаем использование итогов обратно
РегистрИмя.УстановитьИспользованиеИтогов(Истина);
Проанализируем, что происходит на уровне СУБД при выполнении этого кода. При вызове метода УстановитьИспользованиеИтогов(Ложь) платформа отправляет к СУБД команду очистки таблицы итогов этого регистра (фактически выполняется быстрая операция TRUNCATE). При повторном включении метода с параметром Истина платформа заново сканирует только таблицу движений этого регистра и «с нуля» строит чистую, абсолютно правильную таблицу итогов.
Этот метод работает в разы быстрее стандартного пересчета, поскольку исключает выполнение сложных проверок транзакций и накладных расходов СУБД на обновление старых записей.
Если вы точно знаете, с какого момента пошли расхождения в регистре (например, после неудачного программного проведения документов за прошлый месяц), можно выполнить выборочный пересчет с помощью метода ПересчитатьИтогиЗаПериод():
НачалоПериода = '20180601000000'; // 1 июня 2018 года
КонецПериода = '20180630235959'; // 30 июня 2018 года
РегистрыНакопления.АР_ВзаиморасчетыПоСчетамНаАренду.ПересчитатьИтогиЗаПериод(НачалоПериода, КонецПериода);
Этот метод позволяет минимизировать время блокировки таблиц, обновляя итоги только за указанный диапазон дат.
Чтобы минимизировать риски повторного появления проблемы «битых итогов» и задвоения остатков, рекомендуем придерживаться следующих правил при разработке и администрировании систем на базе 1С:
ГДЕ.Мы подробно разобрали причины возникновения фантомных и задвоенных остатков в 1С, изучили внутреннее устройство таблиц хранения регистров и освоили эффективные методики их восстановления. Применяя описанные подходы на практике, вы сможете оперативно устранять подобные технические сбои и обеспечивать стабильную и быструю работу вашей информационной системы.