Как скорректировать бухгалтерские проводки в большом массиве документов 1С: методы оптимизации

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

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

Анализ проблемы производительности

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

  1. Чтение лишних данных: НаборЗаписей.Прочитать() считывает абсолютно все проводки документа. Если в документе сотни строк, а нам нужна одна — мы тратим ресурсы впустую.
  2. Избыточное использование памяти: Выгрузка в ТЗ (НаборЗаписей.Выгрузить()) создает копию данных в оперативной памяти. На десятках тысяч итераций это приводит к фрагментации памяти и утечкам.
  3. Пересчет итогов: При каждой записи набора (НаборЗаписей.Записать()) система по умолчанию пересчитывает итоги регистра, что является самой тяжелой операцией для базы данных SQL.

Шаг 1. Предварительный анализ через запрос

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Хозрасчетный.Регистратор КАК Регистратор,
    |	Хозрасчетный.НомерСтроки КАК НомерСтроки,
    |	Хозрасчетный.Сумма КАК ТекущаяСумма
    |ИЗ
    |	РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
    |ГДЕ
    |	Хозрасчетный.Период МЕЖДУ &НачалоПериода И &КонецПериода
    |	И Хозрасчетный.Субконто1 = &Номенклатура
    |	И Хозрасчетный.Склад = &Склад";

Запрос.УстановитьПараметр("НачалоПериода", НачалоКвартала);
Запрос.УстановитьПараметр("КонецПериода", КонецКвартала);
// Установите остальные параметры отбора
РезультатЗапроса = Запрос.Выполнить().Выгрузить();

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

Шаг 2. Оптимизация записи через метод «Записать(Ложь)»

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

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


Набор = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ВыбранныйРегистратор);

// Важно: НЕ вызываем Набор.Прочитать()!

НоваяЗапись = Набор.Добавить();
НоваяЗапись.Период = ВыбранныйРегистратор.Дата;
НоваяЗапись.Регистратор = ВыбранныйРегистратор;
НоваяЗапись.СчетДт = СчетУчета;
НоваяЗапись.СчетКт = СчетПродаж;
НоваяЗапись.Сумма = РазницаСумм; // Рассчитанная дельта
// Заполняем субконто и другие реквизиты...

// Записываем с параметром Ложь (не замещать существующие записи)
Набор.Записать(Ложь);

Этот метод работает в десятки раз быстрее, так как 1С просто вставляет новую строку в таблицу БД, не анализируя и не удаляя старые записи регистра по этому документу.

Шаг 3. Использование циклов вместо выгрузки в ТЗ

Если же бизнес-логика требует именно правки существующей строки, откажемся от использования Таблицы Значений. Рассмотрим, как правильно перебирать движения в цикле самого набора. Это экономит память и процессорное время.


Набор = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(РегистраторИзЗапроса);
Набор.Прочитать();

Для Каждого Запись Из Набор Цикл
    Если Запись.НомерСтроки = ИскомыйНомерСтроки Тогда
        Запись.Сумма = Запись.Сумма * Коэффициент;
    КонецЕсли;
КонецЦикла;

Набор.Записать();

Проанализируем этот подход: мы избавились от операций Выгрузить() и Загрузить(). Хотя Набор.Прочитать() всё еще используется, отсутствие лишнего копирования данных в ТЗ существенно повышает стабильность системы на больших объемах.

Шаг 4. Управление транзакциями и порционность

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

Разберем алгоритм порционной обработки:

  1. Разделим весь массив документов на пакеты (например, по 500 или 1000 документов).
  2. Каждый пакет будем обрабатывать в отдельной транзакции.
  3. После каждого пакета будем принудительно очищать переменные и вызывать ЗафиксироватьТранзакцию().

Счетчик = 0;
НачатьТранзакцию();

Для Каждого СтрокаПлана Из ТаблицаРегистраторов Цикл
    // Выполняем корректировку проводок...
    
    Счетчик = Счетчик + 1;
    Если Счетчик % 1000 = 0 Тогда
        ЗафиксироватьТранзакцию();
        НачатьТранзакцию();
        // Здесь можно вывести сообщение пользователю для контроля прогресса
    КонецЕсли;
КонецЦикла;

Если ТранзакцияАктивна() Тогда
    ЗафиксироватьТранзакцию();
КонецЕсли;

Такой подход предотвращает бесконечный рост файла лога транзакций SQL-сервера (.ldf) и позволяет избежать блокировок всей базы данных на длительный срок.

Шаг 5. Отключение итогов для максимального ускорения

Если корректировка выполняется в «монопольном» режиме или в период низкой активности пользователей, мы можем применить «тяжелую артиллерию» — временное отключение использования итогов. Когда итоги включены, любая запись проводки заставляет систему обновлять таблицы остатков и оборотов. При массовой правке это создает колоссальную нагрузку.

Рассмотрим последовательность действий:

  1. Программное отключение итогов регистра: РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Ложь);.
  2. Выполнение всех корректировок выбранным методом.
  3. Включение итогов обратно: РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Истина);.
  4. Полный пересчет итогов за корректируемый период.

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

Шаг 6. Параллельная обработка через фоновые задания

Для современных систем на платформе 8.3 лучшим способом оптимизации будет распараллеливание задачи. Мы можем разделить список документов на несколько частей (например, по месяцам или по первой букве кода номенклатуры) и запустить несколько ФоновыхЗаданий. На 8-ядерном сервере 4–6 параллельных потоков завершат работу в 3–5 раз быстрее, чем один последовательный цикл.

Выясним преимущества этого метода:

Резюме и рекомендации

Подведем итог. Чтобы ваша обработка не работала две недели, следуйте этим правилам:

  1. Никогда не перебирайте документы циклом Выбрать(). Используйте Запрос для точечного поиска проводок.
  2. Если возможно, используйте НаборЗаписей.Записать(Ложь) для добавления корректирующих записей без чтения существующих.
  3. Избегайте Таблиц Значений при работе с движениями — работайте напрямую с коллекцией НаборЗаписей.
  4. Обязательно делите работу на порции с фиксацией транзакций через каждые 500–1000 документов.
  5. Следите за состоянием памяти сервера и принудительно обнуляйте объект НаборЗаписей после каждой итерации (Набор = Неопределено;).

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

← На главную