Рассмотрим ситуацию, когда таблица итогов регистра накопления становится аномально большой, превышая размер таблицы движений в десятки раз. Эта проблема часто встречается в информационных базах 1С, особенно в крупных конфигурациях, таких как УПП, и может значительно влиять на производительность системы. Мы выясним причины такого разрастания и предложим комплексные решения, которые помогут оптимизировать работу ваших регистров накопления. Для наглядного выявления наиболее «тяжёлых» таблиц и оценки распределения дискового пространства подойдёт инструмент для анализа карты объёмов базы 1С.
Регистры накопления в 1С предназначены для хранения информации об остатках и оборотах в различных разрезах учета. Они состоят из двух основных таблиц, которые физически хранятся в базе данных:
dbo._AccumRg28133 для регистра "Расходы при УСН") хранит каждую отдельную запись о приходе или расходе по регистру. Это "первичные" данные.dbo._AccumRgT28152) содержит агрегированные данные, необходимые для быстрого получения остатков или оборотов на определенный момент времени. Эти данные, как правило, рассчитываются с определенной периодичностью (обычно месяц) и хранят значения на начало каждого периода по всем уникальным комбинациям измерений.Таблица итогов создается платформой 1С для оптимизации производительности. Вместо того чтобы каждый раз при запросе остатков перебирать все движения за длительный период, система использует уже рассчитанные итоговые значения, что значительно ускоряет работу отчетов и документов. Однако, если таблица итогов разрастается до аномальных размеров, это сигнализирует о наличии проблем в данных или настройках регистра, которые требуют нашего вмешательства.
Мы проанализируем две основные причины, которые чаще всего приводят к данной проблеме, и рассмотрим способы их устранения.
Одной из наиболее частых и значительных причин разрастания таблицы итогов является включенный режим "Разделение итогов" для регистра накопления.
Что такое "Разделение итогов"?
Этот режим разработан для повышения параллельности работы пользователей в многопользовательских системах. Когда несколько пользователей одновременно записывают движения в один и тот же регистр, система, вместо обновления одной общей записи итогов (что могло бы привести к блокировкам), создает несколько отдельных записей. Каждая из этих записей содержит данные по одной и той же комбинации измерений, но с разными значениями специального технического поля-разделителя. При получении остатков платформа автоматически суммирует данные из всех таких "разделенных" записей.
Как это приводит к росту таблицы?
Если в базе данных активно много параллельных операций, то количество таких "разделенных" записей может значительно вырасти. Вместо одной записи остатков за период по конкретной аналитике, мы можем получить десятки или сотни таких записей. В идеале, при стандартном пересчете итогов, эти "разделенные" записи должны сворачиваться в одну. Однако на практике, особенно при длительном периоде без полного пересчета итогов или свертки базы, а также при наличии большого количества активных пользователей, этот механизм может работать неэффективно, приводя к накоплению избыточных данных.
Решение: Отключение режима "Разделение итогов" и пересчет
Практический опыт показывает, что отключение режима "Разделение итогов" и последующий полный пересчет итогов является одним из самых эффективных способов значительного сокращения размера таблицы.
Как показал опыт из обсуждения на форуме, отключение "Разделения итогов" с последующей реструктуризацией и пересчетом дало очень значительный результат: размер таблицы сократился со 164 миллионов записей и 51 ГБ до 5 миллионов записей и 1.6 ГБ.
Другой частой причиной разрастания таблицы итогов являются "висящие" остатки по какой-либо аналитике или старые движения, которые по тем или иным причинам не были закрыты до нуля.
Как это происходит?
Если по определенной комбинации измерений (например, конкретный товар, склад, статья расходов) в регистре возникло движение (приход или расход), и в дальнейшем этот остаток никогда не был обнулен (то есть, не было компенсирующего движения, которое бы полностью закрыло его до нуля), то для каждого последующего периода (месяца), начиная с даты возникновения этого остатка, будет создаваться запись в таблице итогов. Представьте себе одно движение, сделанное, например, в 2000 году, по которому до сих пор числится хотя бы минимальный остаток. За все прошедшие годы (с 2000 по текущий) для каждого месяца будет создана отдельная запись в таблице итогов по этой аналитике. В результате, одно давнее, не закрытое движение может породить сотни записей в таблице итогов.
Это особенно актуально для регистров, где движения могут быть "односторонними" (например, только приход без расхода, или наоборот), или когда используются некорректные даты (например, "нулевые" даты 01.01.0001) в измерениях, что может уводить остатки в глубокое прошлое.
Как выявить такие движения?
Для выявления потенциальных "висящих" остатков или аномально старых движений, мы можем использовать запрос для поиска минимальных и максимальных дат движений по каждому регистру. Это поможет нам определить, есть ли в базе "слишком" старые или "слишком" новые (некорректные) движения, которые могут быть причиной проблемы. Выявив такие даты, мы сможем сфокусироваться на конкретных регистрах и периодах.
Пример кода для поиска минимальной и максимальной даты движений по регистрам накопления:
&НаСервере
Процедура ПоказатьНаСервере()
// Создаем таблицу для хранения минимальных дат движений
ТаблицаМинимальныхДат = Новый ТаблицаЗначений;
ТаблицаМинимальныхДат.Колонки.Добавить("ИмяРегистра");
ТаблицаМинимальныхДат.Колонки.Добавить("МинимальнаяДата", Новый ОписаниеТипов("Дата"));
ТаблицаМинимальныхДат.Колонки.Добавить("МинимальнаяДатаСтрокой");
// Создаем таблицу для хранения максимальных дат движений
ТаблицаМаксимальныхДат = Новый ТаблицаЗначений;
ТаблицаМаксимальныхДат.Колонки.Добавить("ИмяРегистра");
ТаблицаМаксимальныхДат.Колонки.Добавить("МаксимальнаяДата", Новый ОписаниеТипов("Дата"));
ТаблицаМаксимальныхДат.Колонки.Добавить("МаксимальнаяДатаСтрокой");
// Вызываем вспомогательную процедуру для заполнения таблиц
ПоказатьТаблицуНаСервере(ТаблицаМинимальныхДат, "МинимальнаяДата", Ложь); // Ищем минимальные даты
ПоказатьТаблицуНаСервере(ТаблицаМаксимальныхДат, "МаксимальнаяДата", Истина); // Ищем максимальные даты
КонецПроцедуры
&НаСервере
Процедура ПоказатьТаблицуНаСервере(Таблица, ИмяКолонки, ПоУбыванию)
// Определяем суффикс для сортировки в запросе
СуффиксСортировки = ?(ПоУбыванию, " Убыв", "");
Таблица.Очистить(); // Очищаем таблицу перед заполнением
// Перебираем все регистры накопления в метаданных
Для Каждого Метаданное Из Метаданные.РегистрыНакопления Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Регистр.Период КАК Период
|ИЗ
| РегистрНакопления." + Метаданное.Имя + " КАК Регистр
|
|УПОРЯДОЧИТЬ ПО
| Период" + СуффиксСортировки; // Сортируем по Периоду для получения первой (минимальной) или последней (максимальной) даты
Выборка = Запрос.Выполнить().Выбрать();
// Если в регистре есть движения
Если Выборка.Следующий() Тогда
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.ИмяРегистра = Метаданное.Синоним; // Синоним регистра для удобства
НоваяСтрока[ИмяКолонки] = Выборка.Период; // Сама дата
НоваяСтрока[ИмяКолонки + "Строкой"] = Выборка.Период; // Дата в строковом представлении
КонецЕсли;
КонецЦикла;
// Сортируем итоговую таблицу
Таблица.Сортировать(ИмяКолонки + СуффиксСортировки);
КонецПроцедуры
Решение:
Дополнительная проверка на "нулевые записи":
Иногда причиной большого объема могут быть записи в таблице итогов, у которых все ресурсы равны нулю. Эти записи также должны сворачиваться при пересчете итогов, но иногда остаются. Их можно попытаться найти прямым запросом к СУБД (с большой осторожностью и только для анализа, не для изменения данных!):
SELECT count(*)
FROM [dbo].[_AccumRgT28152] -- Замените на фактическое имя таблицы итогов вашего регистра
WHERE [_Fld28147] = 0 AND [_Fld28148] = 0 AND [_Fld28149] = 0; -- Замените поля на фактические ресурсы вашего регистра
В этом запросе _Fld28147, _Fld28148, _Fld28149 — это примеры имен полей ресурсов регистра в физической таблице базы данных. Аккуратно определите фактические имена полей ресурсов для вашего регистра, используя, например, функцию ПолучитьСтруктуруХраненияБазыДанных(). Для быстрого просмотра соответствия объектов метаданных и физических таблиц СУБД можно использовать обработку для анализа структуры хранения базы данных.
В дополнение к вышеописанным мерам, существуют общие рекомендации, которые помогут поддерживать регистры накопления в оптимальном состоянии и предотвращать их разрастание:
Регистр накопления "Расходы при УСН", который был рассмотрен в теме, используется в УПП для налогового учета расходов в целях УСН. Его корректное ведение критически важно для формирования Книги учета доходов и расходов и правильного расчета налога по УСН. Вид движения "Приход" в этом регистре обычно означает возникновение затраты, а "Расход" — ее признание в составе расходов по УСН при выполнении определенных условий. Проблемы с этим регистром могут не только замедлять работу системы, но и приводить к ошибкам в налоговой отчетности. Поэтому особое внимание следует уделять своевременному и корректному закрытию движений в этом регистре, чтобы избежать накопления "висящих" остатков и излишнего роста таблицы итогов.
Заключение:
Разрастание таблицы итогов регистра накопления – это серьезная проблема производительности, но, как мы выяснили, она решаема. Основными виновниками могут быть режим "Разделение итогов" и "висящие" остатки по старым или некорректным движениям. Комплексный подход, включающий отключение разделения итогов, исправление данных, управление параметрами итогов через обработку "Управление итогами", а также регулярное обслуживание базы данных с помощью ТИИ и свертки, поможет поддерживать вашу систему 1С в оптимальном состоянии. Всегда помните о необходимости резервного копирования перед внесением серьезных изменений.