Очистка таблиц итогов регистров остатков от лишних записей в 1с

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

Материал опубликован на сайте SoftPoint.Ru/

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

Фактически, 1С нормально обходится без такой записи. Скорее наоборот, ей приходится эти записи фильтровать. Как же они появляются? А появляются они в момент проведения документа по составу измерений, по которому документ делает движения. Такие «нулевые записи» характеры для 1с регистров, в которых сильно меняется набор измерений – т.е. часть измерений является реквизитом типа «Документ». Хорошим примером является регистр партий. По нему в течение периода итогов (скажем месяц) пришло несколько десятков партий товара, при этом на конец периода осталась, как правило, одна или несколько партий (обычный вариант списания по FIFO). Но по мере работы базы, изо дня в день в некоторый момент времени каждая из партий имелась в наличии, и при этом оставила след в таблице итогов регистра на данный период. Так, как данное сочетание измерений к концу периода закончилось, (партия была распродана), то в таблице итогов по данному сочетанию измерений остались одни нули.

📑 Восемь новых ФСБУ
🔹Изучите все актуальные ФСБУ и подготовьтесь к изменениям 2025 года. Защитите себя от штрафов и ошибок в бухгалтерии. 
🎓 Курс с выгодой до 72% — сейчас всего 6 900 ₽!

Это мы берем пример работы в последнем по времени периоде итогов. А если перепровести документ годичной давности? Тогда во всех последующих периодах, начиная с текущего, по каждому из наборов измерений присутствующих в движениях данного документа в таблице итогов будут присутствовать данные наборы измерений. И если партия закончилась сразу же, то, значит, все они будут нулевыми. Если в приходной накладной 100 строк, то с учетом годичной давности мы получим 1200 лишних записей в таблице итогов регистра.

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

Я произвел замеры на своей базе по регистру «Заказы». В этом регистре учитываются заказы товара поставщику, которые снимаются приходом товара или отменяются. В общем, и целом – полная аналогия с регистром резервов. Так вот – из порядка 900000 записей в таблице итогов из них пустыми были порядка 880000, то есть полезный объем (20000) составлял от полного объема около 2,5%. Остальные данные были излишними…

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

Обработка работает только для баз в формате SQL. Написана с использованием Rainbow. Если у вас нет компоненты Rainbow, то ее можно загрузить здесь. Используйте обработку на свой страх и риск.

Текст модуля можно посмотреть здесь:

Перем ТекМета;
Перем СтрокаСообщения;
//________________________________________________________
Процедура ЧиститьРегистры()
    Если Вопрос(СтрокаСообщения,4)=6 Тогда
        ЗапросРадуги=СоздатьОбъект("ODBCQuery");
        Для к=1 по Метаданные.Регистр() Цикл
            Если Метаданные.Регистр(к).ТипРегистра="Остатки" Тогда
                ТекИдентификатор=Метаданные.Регистр(к).Идентификатор;
                ТекСиноним=Метаданные.Регистр(к).Синоним;
                Если ТекСиноним="" Тогда
                    ТекСиноним=ТекИдентификатор;
                КонецЕсли;
                ИмяТаблицы=ТекМета.ИмяТаблицыИтогов(ТекИдентификатор);
                ТекстЗапроса="DELETE
                |FROM "+ИмяТаблицы+"
                |WHERE ";
                Для й=1 по Метаданные.Регистр(к).Ресурс() Цикл
                    ТекИдРесурса=Метаданные.Регистр(к).Ресурс(й).Идентификатор;
                    ТекстЗапроса=ТекстЗапроса+?(й=1,""," AND ")+"(SP"+ТекМета.ИдРесурсаРегистра(ТекИдентификатор,ТекИдРесурса)+"=0)";
                КонецЦикла;
                //Теперь выполняем запрос
                Если ЗапросРадуги.Prepare(ТекстЗапроса,1,1)=1 Тогда
                    Если ЗапросРадуги.Open()=1 Тогда
                        Сообщить("Обработан регистр <"+ТекСиноним+">","i");
                        ЗапросРадуги.Close();
                    Иначе
                        Предупреждение("Ошибка открытия запроса!",10);
                    КонецЕсли;
                    ЗапросРадуги.Reset();
                Иначе
                    Предупреждение("Ошибка выполнения запроса!",10);
                КонецЕсли;
            КонецЕсли;
        КонецЦикла;
        ЗапросРадуги="";
    КонецЕсли;
КонецПроцедуры
//________________________________________________________
Процедура ПосчитатьРегистры()
    ЗапросРадуги=СоздатьОбъект("ODBCQuery");
    Для к=1 по Метаданные.Регистр() Цикл
        Если Метаданные.Регистр(к).ТипРегистра="Остатки" Тогда
            ТекИдентификатор=Метаданные.Регистр(к).Идентификатор;
            ТекСиноним=Метаданные.Регистр(к).Синоним;
            Если ТекСиноним="" Тогда
                ТекСиноним=ТекИдентификатор;
            КонецЕсли;
            ИмяТаблицы=ТекМета.ИмяТаблицыИтогов(ТекИдентификатор);
            ТекстЗапроса="SELECT COUNT(*)
            |FROM "+ИмяТаблицы+"
            |WHERE ";
            Для й=1 по Метаданные.Регистр(к).Ресурс() Цикл
                ТекИдРесурса=Метаданные.Регистр(к).Ресурс(й).Идентификатор;
                ТекстЗапроса=ТекстЗапроса+?(й=1,""," AND ")+"(SP"+ТекМета.ИдРесурсаРегистра(ТекИдентификатор,ТекИдРесурса)+"=0)";
            КонецЦикла;
            //Выполняем запрос на общее количество
            ПолноеЧисло=0;
            Если ЗапросРадуги.Prepare("SELECT COUNT(*) FROM "+ИмяТаблицы,1,1)=1 Тогда
                Если ЗапросРадуги.Open()=1 Тогда
                    ЗапросРадуги.GotoNext();
                    Если ЗапросРадуги.IsOK()=1 Тогда
                        ПолноеЧисло=ЗапросРадуги.GetLong(0);
                    КонецЕсли;
                    ЗапросРадуги.Close();
                Иначе
                    Предупреждение("Ошибка открытия запроса!",10);
                КонецЕсли;
                ЗапросРадуги.Reset();
            Иначе
                Предупреждение("Ошибка выполнения запроса!",10);
            КонецЕсли;
            //Теперь выполняем запрос на количество пустых
            ПустоеЧисло=0;
            Если ЗапросРадуги.Prepare(ТекстЗапроса,1,1)=1 Тогда
                Если ЗапросРадуги.Open()=1 Тогда
                    ЗапросРадуги.GotoNext();
                    Если ЗапросРадуги.IsOK()=1 Тогда
                        ПустоеЧисло=ЗапросРадуги.GetLong(0);
                    КонецЕсли;
                    ЗапросРадуги.Close();
                Иначе
                    Предупреждение("Ошибка открытия запроса!",10);
                КонецЕсли;
                ЗапросРадуги.Reset();
            Иначе
                Предупреждение("Ошибка выполнения запроса!",10);
            КонецЕсли;
            Сообщить("Пустых строк итогов по регистру <"+ТекСиноним+"> : "+ПустоеЧисло+" из "+ ПолноеЧисло+" возможных.","i");
        КонецЕсли;
    КонецЦикла;
    ЗапросРадуги="";
КонецПроцедуры
//________________________________________________________
Процедура ЧиститьБухучет()
    Если Метаданные.ОсновнойПланСчетов.Выбран()=0 Тогда
        Предупреждение("В данной конфигурации бухгалтерский учет не ведется!!!",20);
        Возврат;
    КонецЕсли;
    Если Вопрос(СтрокаСообщения,4)=6 Тогда
        ЗапросРадуги=СоздатьОбъект("ODBCQuery");
        ТекстЗапроса="DELETE
        |FROM _1SBKTTL
        |WHERE (OBDT1 = 0) AND (OBKT1 = 0) AND (OBDT2 = 0) AND (OBKT2 = 0) AND (OBDT3 = 0) AND (OBKT3 = 0) AND (SD = 0)";
        Если ЗапросРадуги.Prepare(ТекстЗапроса,1,1)=1 Тогда
            Если ЗапросРадуги.Open()=1 Тогда
                Сообщить("Обработана таблица остатков (сальдо и обороты по субконто)","i");
                ЗапросРадуги.Close();
            Иначе
                Предупреждение("Ошибка открытия запроса!",10);
            КонецЕсли;
            ЗапросРадуги.Reset();
        Иначе
            Предупреждение("Ошибка выполнения запроса!",10);
        КонецЕсли;
        ТекстЗапроса="DELETE
        |FROM _1SBKTTLC
        |WHERE (OB1 = 0) AND (OB2 = 0) AND (OB3 = 0)";
        Если ЗапросРадуги.Prepare(ТекстЗапроса,1,1)=1 Тогда
            Если ЗапросРадуги.Open()=1 Тогда
                Сообщить("Обработана таблица итогов (обороты по синтетическим счетам)","i");
                ЗапросРадуги.Close();
            Иначе
                Предупреждение("Ошибка открытия запроса!",10);
            КонецЕсли;
            ЗапросРадуги.Reset();
        Иначе
            Предупреждение("Ошибка выполнения запроса!",10);
        КонецЕсли;
        ЗапросРадуги="";
    КонецЕсли;
КонецПроцедуры
//________________________________________________________
Процедура ПосчитатьБухучет()
    Если Метаданные.ОсновнойПланСчетов.Выбран()=0 Тогда
        Предупреждение("В данной конфигурации бухгалтерский учет не ведется!!!",20);
        Возврат;
    КонецЕсли;
    ЗапросРадуги=СоздатьОбъект("ODBCQuery");
    //Сначала таблица остатков (обороты по субконто и сальдо)

    //Выполняем запрос на общее количество
    ПолноеЧисло=0;
    Если ЗапросРадуги.Prepare("SELECT COUNT(*) FROM _1SBKTTL",1,1)=1 Тогда
        Если ЗапросРадуги.Open()=1 Тогда
            ЗапросРадуги.GotoNext();
            Если ЗапросРадуги.IsOK()=1 Тогда
                ПолноеЧисло=ЗапросРадуги.GetLong(0);
            КонецЕсли;
            ЗапросРадуги.Close();
        Иначе
            Предупреждение("Ошибка открытия запроса!",10);
        КонецЕсли;
        ЗапросРадуги.Reset();
    Иначе
        Предупреждение("Ошибка выполнения запроса!",10);
    КонецЕсли;

    //Теперь выполняем запрос на количество пустых
    ПустоеЧисло=0;
    ТекстЗапроса="SELECT COUNT(*)
    |FROM _1SBKTTL
    |WHERE (OBDT1 = 0) AND (OBKT1 = 0) AND (OBDT2 = 0) AND (OBKT2 = 0) AND (OBDT3 = 0) AND (OBKT3 = 0) AND (SD = 0)";
    Если ЗапросРадуги.Prepare(ТекстЗапроса,1,1)=1 Тогда
        Если ЗапросРадуги.Open()=1 Тогда
            ЗапросРадуги.GotoNext();
            Если ЗапросРадуги.IsOK()=1 Тогда
                ПустоеЧисло=ЗапросРадуги.GetLong(0);
            КонецЕсли;
            ЗапросРадуги.Close();
        Иначе
            Предупреждение("Ошибка открытия запроса!",10);
        КонецЕсли;
        ЗапросРадуги.Reset();
    Иначе
        Предупреждение("Ошибка выполнения запроса!",10);
    КонецЕсли;
    Сообщить("Пустых строк в таблице остатков (сальдо и обороты по субконто): "+ПустоеЧисло+" из "+ ПолноеЧисло+" возможных.","i");

    //Теперь таблица итогов (обороты по синтетическим счетам)

    //Выполняем запрос на общее количество
    ПолноеЧисло=0;
    Если ЗапросРадуги.Prepare("SELECT COUNT(*) FROM _1SBKTTLC",1,1)=1 Тогда
        Если ЗапросРадуги.Open()=1 Тогда
            ЗапросРадуги.GotoNext();
            Если ЗапросРадуги.IsOK()=1 Тогда
                ПолноеЧисло=ЗапросРадуги.GetLong(0);
            КонецЕсли;
            ЗапросРадуги.Close();
        Иначе
            Предупреждение("Ошибка открытия запроса!",10);
        КонецЕсли;
        ЗапросРадуги.Reset();
    Иначе
        Предупреждение("Ошибка выполнения запроса!",10);
    КонецЕсли;

    //Теперь выполняем запрос на количество пустых
    ПустоеЧисло=0;
    ТекстЗапроса="SELECT COUNT(*)
    |FROM _1SBKTTLC
    |WHERE (OB1 = 0) AND (OB2 = 0) AND (OB3 = 0)";
    Если ЗапросРадуги.Prepare(ТекстЗапроса,1,1)=1 Тогда
        Если ЗапросРадуги.Open()=1 Тогда
            ЗапросРадуги.GotoNext();
            Если ЗапросРадуги.IsOK()=1 Тогда
                ПустоеЧисло=ЗапросРадуги.GetLong(0);
            КонецЕсли;
            ЗапросРадуги.Close();
        Иначе
            Предупреждение("Ошибка открытия запроса!",10);
        КонецЕсли;
        ЗапросРадуги.Reset();
    Иначе
        Предупреждение("Ошибка выполнения запроса!",10);
    КонецЕсли;
    Сообщить("Пустых строк в таблице итогов (обороты по синтетическим счетам): "+ПустоеЧисло+" из "+ ПолноеЧисло+" возможных.","i");
    ЗапросРадуги="";
КонецПроцедуры
//________________________________________________________
Процедура ПриОткрытии()
    Попытка
        ЗагрузитьВнешнююКомпоненту("rainbow.dll");
        ТекМета=СоздатьОбъект("MetaDataWork");
    Исключение
        Форма.кнПосчитатьРегистры.Доступность(0);
        Форма.кнЧиститьРегистры.Доступность(0);
        Форма.кнПосчитатьБухучет.Доступность(0);
        Форма.кнЧиститьБухучет.Доступность(0);
    КонецПопытки;
    СтрокаСообщения="Удаление записей из таблицы итогов - необратимый процесс!
    |Отменить данную операцию после выполнения будет невозможно!
    |Вы уверены, что хотите продолжить?";
КонецПроцедуры

Начать дискуссию