Очистка таблицы значений в 1С: удаление строк, колонок и памяти

· 9 мин

Содержание

Чтобы удалить все строки таблицы значений и оставить структуру колонок — вызовите ТЗ.Очистить(). Если нужно освободить память под большой таблицей — присвойте переменной новый объект через ТЗ = ТЗ.СкопироватьКолонки(). А когда из таблицы надо убрать только часть строк по условию — идите по ней обратным циклом и вызывайте Удалить(Индекс).

Это три разных задачи, и путать их не стоит: Очистить() чистит строки, но колонки и выделенная под них память остаются; пересоздание объекта возвращает чистую таблицу с теми же колонками; а удаление в прямом цикле Для Каждого вообще приводит к пропуску строк. Ниже — когда какой способ применять, с готовыми примерами кода и замерами.

Метод Очистить(): быстрое удаление всех строк

Самый частый случай — очистить таблицу значений целиком, сохранив набор колонок, чтобы заполнять её заново. Для этого есть штатный метод:

ТЗ.Очистить();

После вызова ТЗ.Количество() вернёт 0, но коллекция ТЗ.Колонки останется нетронутой — типы, имена и заголовки колонок на месте. Это ровно то, что нужно, когда таблица используется как буфер: заполнили, обработали, очистили, заполнили снова.

Очистить() работает мгновенно независимо от числа строк — он не перебирает их по одной, а сбрасывает внутренний массив целиком.

Важное отличие: ТЗ.Очистить() убирает строки, а ТЗ.Колонки.Очистить() убирает колонки. Это разные коллекции с одинаковым именем метода:

ТЗ.Очистить();          // удалены все строки, колонки остались
ТЗ.Колонки.Очистить();  // удалены все колонки (вместе с данными)

Если выполнить оба вызова, получится полностью пустая таблица без структуры. Обычно так не делают — проще создать новый объект.

Пересоздание объекта: когда нужно освободить память

Очистить() сбрасывает строки, но объект остаётся тем же, и память под ранее выделенными данными освобождается не сразу. Если таблица была огромной (сотни тысяч строк с длинными строковыми значениями), а дальше по коду она уже не нужна в прежнем виде — надёжнее пересоздать объект:

ТЗ = Новый ТаблицаЗначений;

Минус очевиден: новая таблица будет без колонок, структуру придётся задавать заново. Чтобы сохранить колонки и при этом получить свежий пустой объект, используйте СкопироватьКолонки():

ТЗ = ТЗ.СкопироватьКолонки();

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

После СкопироватьКолонки() это другой объект. Если на старую таблицу ссылались из других переменных или реквизитов формы — они продолжат указывать на прежние данные. Обновите все ссылки.

Когда что выбирать:

  • Очистить() — таблица будет наполняться снова в том же цикле обработки, память не критична.
  • СкопироватьКолонки() — нужен чистый объект с той же структурой и важно освободить память.
  • Новый ТаблицаЗначений — структура колонок всё равно задаётся заново ниже по коду.
Что именно нужно сделать с таблицей значений?

Выборочное удаление строк: обратный цикл и НайтиСтроки

Когда из таблицы нужно убрать не всё, а строки по условию, работает не очистка, а удаление. Здесь главная ловушка — направление перебора.

Если идти прямым циклом Для Каждого и удалять строки внутри него, 1С выдаст ошибку изменения коллекции во время обхода либо часть строк будет пропущена: после удаления строки индексы сдвигаются, и перебор «перепрыгивает» соседнюю. Удалять строки в прямом цикле по той же таблице нельзя.

Правильный приём — идти с конца к началу по индексу. При удалении последних строк сдвиг не затрагивает ещё не пройденные:

Для Сч = ТЗ.Количество() - 1 По 0 Цикл
    Строка = ТЗ[Сч];
    Если Строка.Сумма = 0 Тогда
        ТЗ.Удалить(Сч);
    КонецЕсли;
КонецЦикла;

Цикл начинается с последнего индекса (Количество() - 1) и спускается к нулю. Удаление по индексу ТЗ.Удалить(Сч) здесь безопасно.

Второй способ — сначала найти нужные строки, потом удалить их по ссылке. НайтиСтроки() возвращает массив ссылок на строки, и этот массив не зависит от таблицы, поэтому удалять по нему можно в прямом порядке:

Отбор = Новый Структура("Статус", "Отменён");
СтрокиКУдалению = ТЗ.НайтиСтроки(Отбор);
Для Каждого Строка Из СтрокиКУдалению Цикл
    ТЗ.Удалить(Строка);
КонецЦикла;

НайтиСтроки() принимает структуру с отбором по значениям колонок. Это читаемее обратного цикла, когда условие — простое равенство. Для сложных условий (диапазоны, сравнения) подойдёт обратный цикл с Если.

Удалить() принимает и индекс (число), и саму строку (объект СтрокаТаблицыЗначений). Оба варианта корректны, выбирайте по удобству.

Сравнение скорости: что быстрее очистить таблицу

Три подхода к полной очистке отличаются по скорости на порядки. Ориентиры для таблицы примерно в 100 000 строк:

СпособЧто делаетСкорость
ТЗ.Очистить()сброс всех строк, колонки остаютсямгновенно
ТЗ.СкопироватьКолонки()новый объект с теми же колонкамимгновенно
Обратный цикл Удалить(Сч)удаление строк по однойв сотни раз медленнее

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

Цикл оправдан только для выборочного удаления, когда убрать нужно действительно часть строк. Но и здесь, если под условие попадает больше половины таблицы, часто быстрее собрать нужные строки в новую таблицу, чем удалять лишние:

НоваяТЗ = ТЗ.СкопироватьКолонки();
Для Каждого Строка Из ТЗ Цикл
    Если Строка.Сумма <> 0 Тогда
        ЗаполнитьЗначенияСвойств(НоваяТЗ.Добавить(), Строка);
    КонецЕсли;
КонецЦикла;
ТЗ = НоваяТЗ;

Здесь мы копируем в новую таблицу только то, что оставляем. Один проход, без сдвигов — на больших объёмах с массовым удалением это выигрывает.

Таблица значений и временные таблицы запроса: не путать

Отдельная тема — временные таблицы в языке запросов. Это не объект ТаблицаЗначений в памяти, а именованная таблица внутри МенеджерВременныхТаблиц, и «очищают» её иначе.

Таблицу значений можно поместить во временную таблицу запроса оператором ПОМЕСТИТЬ:

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
|   Данные.Номенклатура,
|   Данные.Количество
|ПОМЕСТИТЬ ВремДанные
|ИЗ
|   &ИсходнаяТаблица КАК Данные";
Запрос.УстановитьПараметр("ИсходнаяТаблица", ТЗ);
Запрос.Выполнить();

Чтобы «очистить» такую таблицу, её уничтожают оператором УНИЧТОЖИТЬ в тексте следующего запроса:

УНИЧТОЖИТЬ ВремДанные

А чтобы сбросить все временные таблицы менеджера разом, закрывают сам менеджер:

Запрос.МенеджерВременныхТаблиц.Закрыть();

После Закрыть() все временные таблицы менеджера удаляются, и повторно обращаться к ним нельзя. Это и есть полная очистка временного хранилища запроса — в отличие от Очистить(), который работает с объектом в оперативной памяти.

Короткое правило: ТаблицаЗначений в памяти чистят методами Очистить() / пересозданием, а временные таблицы запроса — операторами УНИЧТОЖИТЬ или методом Закрыть() у менеджера.

Частые ошибки при очистке таблицы значений

Три промаха, на которых спотыкаются чаще всего:

  • Удаление в прямом цикле. Перебор Для Каждого с ТЗ.Удалить() внутри пропускает строки или падает с ошибкой. Решение — обратный цикл по индексу или предварительный НайтиСтроки().
  • Путаница Очистить() и Колонки.Очистить(). Первый убирает строки, второй — колонки. Если после очистки «пропала структура», значит вызван не тот метод.
  • Ожидание мгновенного освобождения памяти от Очистить(). Метод сбрасывает строки, но объект живёт. Если держите ссылку на большую таблицу, память не вернётся, пока ссылка жива. Нужна память — пересоздайте объект.
Диагностика: выберите симптом

Очистка таблицы значений на управляемой форме

Отдельный случай — таблица значений как реквизит управляемой формы. Метод Очистить() здесь тоже работает, причём вызвать его можно и на клиенте, и на сервере:

&НаКлиенте
Процедура ОчиститьТаблицу(Команда)
    ТаблицаНаФорме.Очистить();
КонецПроцедуры

Строки уйдут, колонки реквизита останутся — ровно как у таблицы в памяти. Это самый частый сценарий: кнопка «Очистить» на форме просто сбрасывает строки.

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

&НаСервере
Процедура ЗаполнитьНаСервере()
    НоваяТаблица = ПолучитьТаблицуИзЗапроса();
    ЗначениеВРеквизитФормы(НоваяТаблица, "ТаблицаНаФорме");
КонецПроцедуры

Обратное преобразование — РеквизитФормыВЗначение("ТаблицаНаФорме") — вернёт из реквизита обычный объект ТаблицаЗначений, с которым удобно работать в коде сервера (сортировать, сворачивать, чистить), а затем записать назад.

Оба метода, ЗначениеВРеквизитФормы() и РеквизитФормыВЗначение(), доступны только на сервере. С клиента их не вызвать — выносите логику в процедуру с директивой &НаСервере.

Если таблица — часть объекта (табличная часть документа или обработки), очистка та же: Объект.ТабличнаяЧасть.Очистить(). У табличных частей Очистить() тоже убирает строки и сохраняет колонки.

Отладка: как проверить, что таблица очистилась

Самый быстрый способ убедиться в результате — посмотреть Количество() в отладчике. Поставьте точку останова после очистки и в окне вычисления выражения выполните ТЗ.Количество() — должно быть 0.

Чтобы разом увидеть содержимое таблицы в отладке, в табло или окне вычисления раскройте переменную ТЗ: платформа покажет строки и колонки в виде таблицы. Если строк нет, а колонки видны — Очистить() отработал верно.

Для контроля в логике кода пригодится свойство количества:

Если ТЗ.Количество() = 0 Тогда
    // таблица пуста, можно заполнять заново
КонецЕсли;

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

Типичная причина раздувания памяти в длинных обработках — таблицу-буфер объявили один раз, наполняют порциями, но забывают очищать между порциями. В итоге строки накапливаются, и к концу обработки в таблице не один пакет, а все сразу. Правило простое: таблица-буфер очищается в начале каждой итерации обработки, а не в конце:

Для Каждого Пакет Из ПакетыДанных Цикл
    Буфер.Очистить();
    ЗаполнитьБуфер(Буфер, Пакет);
    ОбработатьБуфер(Буфер);
КонецЦикла;

Очистка в начале цикла надёжнее очистки в конце: даже если обработка пакета прервётся с ошибкой и до конца итерации дело не дойдёт, следующий проход всё равно начнёт с чистого буфера.

Частые вопросы

Чем отличается Очистить() от присвоения нового объекта?
Очистить() оставляет тот же объект и его колонки, просто убирает строки — память под объект сохраняется. Присвоение Новый ТаблицаЗначений или СкопироватьКолонки() создаёт другой объект, а старый уходит под сборку мусора. Для повторного наполнения берите Очистить(), для освобождения памяти — пересоздание.
Как очистить только колонки, не трогая строки?
Полностью убрать колонки без строк нельзя — колонки хранят данные строк. Можно удалить отдельную колонку: ТЗ.Колонки.Удалить("ИмяКолонки"). Удаление всех колонок через ТЗ.Колонки.Очистить() обнулит и данные.
Почему при удалении строк в цикле часть остаётся?
Прямой цикл перебирает строки по порядку, а после удаления индексы смещаются, и следующая строка «перепрыгивается». Идите обратным циклом от последнего индекса к нулю либо используйте НайтиСтроки() и удаляйте по полученному массиву.
Как удалить строки по условию, а не все подряд?
Два варианта: обратный цикл Для Сч = ТЗ.Количество() - 1 По 0 Цикл с проверкой условия и ТЗ.Удалить(Сч), либо ТЗ.НайтиСтроки(Новый Структура("Колонка", Значение)) с перебором результата. Второй удобнее для простого равенства.
Нужно ли очищать таблицу значений вручную перед уничтожением?
Нет. Когда на таблицу не остаётся ссылок, платформа освобождает память автоматически. Ручная очистка нужна только для повторного использования того же объекта или чтобы ускорить возврат памяти в пределах длинной процедуры.