Очистка таблицы значений в 1С: удаление строк, колонок и памяти
Содержание
Чтобы удалить все строки таблицы значений и оставить структуру колонок — вызовите ТЗ.Очистить(). Если нужно освободить память под большой таблицей — присвойте переменной новый объект через ТЗ = ТЗ.СкопироватьКолонки(). А когда из таблицы надо убрать только часть строк по условию — идите по ней обратным циклом и вызывайте Удалить(Индекс).
Это три разных задачи, и путать их не стоит: Очистить() чистит строки, но колонки и выделенная под них память остаются; пересоздание объекта возвращает чистую таблицу с теми же колонками; а удаление в прямом цикле Для Каждого вообще приводит к пропуску строк. Ниже — когда какой способ применять, с готовыми примерами кода и замерами.
Метод Очистить(): быстрое удаление всех строк
Самый частый случай — очистить таблицу значений целиком, сохранив набор колонок, чтобы заполнять её заново. Для этого есть штатный метод:
ТЗ.Очистить();
После вызова ТЗ.Количество() вернёт 0, но коллекция ТЗ.Колонки останется нетронутой — типы, имена и заголовки колонок на месте. Это ровно то, что нужно, когда таблица используется как буфер: заполнили, обработали, очистили, заполнили снова.
Очистить() работает мгновенно независимо от числа строк — он не перебирает их по одной, а сбрасывает внутренний массив целиком.
Важное отличие: ТЗ.Очистить() убирает строки, а ТЗ.Колонки.Очистить() убирает колонки. Это разные коллекции с одинаковым именем метода:
ТЗ.Очистить(); // удалены все строки, колонки остались
ТЗ.Колонки.Очистить(); // удалены все колонки (вместе с данными)
Если выполнить оба вызова, получится полностью пустая таблица без структуры. Обычно так не делают — проще создать новый объект.
Пересоздание объекта: когда нужно освободить память
Очистить() сбрасывает строки, но объект остаётся тем же, и память под ранее выделенными данными освобождается не сразу. Если таблица была огромной (сотни тысяч строк с длинными строковыми значениями), а дальше по коду она уже не нужна в прежнем виде — надёжнее пересоздать объект:
ТЗ = Новый ТаблицаЗначений;
Минус очевиден: новая таблица будет без колонок, структуру придётся задавать заново. Чтобы сохранить колонки и при этом получить свежий пустой объект, используйте СкопироватьКолонки():
ТЗ = ТЗ.СкопироватьКолонки();
Метод возвращает новую таблицу значений с теми же колонками (имена, типы, заголовки, ширина), но без единой строки. Старый объект теряет последнюю ссылку и попадает под сборку мусора — память освобождается.
После СкопироватьКолонки() это другой объект. Если на старую таблицу ссылались из других переменных или реквизитов формы — они продолжат указывать на прежние данные. Обновите все ссылки.
Когда что выбирать:
Очистить()— таблица будет наполняться снова в том же цикле обработки, память не критична.СкопироватьКолонки()— нужен чистый объект с той же структурой и важно освободить память.Новый ТаблицаЗначений— структура колонок всё равно задаётся заново ниже по коду.
ТЗ.Очистить() — быстро, колонки сохраняются.ТЗ = ТЗ.СкопироватьКолонки().Для Сч = ТЗ.Количество() - 1 По 0 Цикл с ТЗ.Удалить(Сч) либо через НайтиСтроки().ТЗ = Новый ТаблицаЗначений и задайте колонки заново.Выборочное удаление строк: обратный цикл и НайтиСтроки
Когда из таблицы нужно убрать не всё, а строки по условию, работает не очистка, а удаление. Здесь главная ловушка — направление перебора.
Если идти прямым циклом Для Каждого и удалять строки внутри него, 1С выдаст ошибку изменения коллекции во время обхода либо часть строк будет пропущена: после удаления строки индексы сдвигаются, и перебор «перепрыгивает» соседнюю. Удалять строки в прямом цикле по той же таблице нельзя.
Правильный приём — идти с конца к началу по индексу. При удалении последних строк сдвиг не затрагивает ещё не пройденные:
Для Сч = ТЗ.Количество() - 1 По 0 Цикл
Строка = ТЗ[Сч];
Если Строка.Сумма = 0 Тогда
ТЗ.Удалить(Сч);
КонецЕсли;
КонецЦикла;
Цикл начинается с последнего индекса (Количество() - 1) и спускается к нулю. Удаление по индексу ТЗ.Удалить(Сч) здесь безопасно.
Второй способ — сначала найти нужные строки, потом удалить их по ссылке. НайтиСтроки() возвращает массив ссылок на строки, и этот массив не зависит от таблицы, поэтому удалять по нему можно в прямом порядке:
Отбор = Новый Структура("Статус", "Отменён");
СтрокиКУдалению = ТЗ.НайтиСтроки(Отбор);
Для Каждого Строка Из СтрокиКУдалению Цикл
ТЗ.Удалить(Строка);
КонецЦикла;
НайтиСтроки() принимает структуру с отбором по значениям колонок. Это читаемее обратного цикла, когда условие — простое равенство. Для сложных условий (диапазоны, сравнения) подойдёт обратный цикл с Если.
Удалить() принимает и индекс (число), и саму строку (объект СтрокаТаблицыЗначений). Оба варианта корректны, выбирайте по удобству.
Сравнение скорости: что быстрее очистить таблицу
Три подхода к полной очистке отличаются по скорости на порядки. Ориентиры для таблицы примерно в 100 000 строк:
| Способ | Что делает | Скорость |
|---|---|---|
ТЗ.Очистить() | сброс всех строк, колонки остаются | мгновенно |
ТЗ.СкопироватьКолонки() | новый объект с теми же колонками | мгновенно |
Обратный цикл Удалить(Сч) | удаление строк по одной | в сотни раз медленнее |
Вывод простой: для полной очистки никогда не используйте цикл. Очистить() и СкопироватьКолонки() работают за константное время, а поштучное удаление в цикле — за квадратичное, потому что каждое Удалить() сдвигает хвост внутреннего массива.
Цикл оправдан только для выборочного удаления, когда убрать нужно действительно часть строк. Но и здесь, если под условие попадает больше половины таблицы, часто быстрее собрать нужные строки в новую таблицу, чем удалять лишние:
НоваяТЗ = ТЗ.СкопироватьКолонки();
Для Каждого Строка Из ТЗ Цикл
Если Строка.Сумма <> 0 Тогда
ЗаполнитьЗначенияСвойств(НоваяТЗ.Добавить(), Строка);
КонецЕсли;
КонецЦикла;
ТЗ = НоваяТЗ;
Здесь мы копируем в новую таблицу только то, что оставляем. Один проход, без сдвигов — на больших объёмах с массовым удалением это выигрывает.
Таблица значений и временные таблицы запроса: не путать
Отдельная тема — временные таблицы в языке запросов. Это не объект ТаблицаЗначений в памяти, а именованная таблица внутри МенеджерВременныхТаблиц, и «очищают» её иначе.
Таблицу значений можно поместить во временную таблицу запроса оператором ПОМЕСТИТЬ:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| Данные.Номенклатура,
| Данные.Количество
|ПОМЕСТИТЬ ВремДанные
|ИЗ
| &ИсходнаяТаблица КАК Данные";
Запрос.УстановитьПараметр("ИсходнаяТаблица", ТЗ);
Запрос.Выполнить();
Чтобы «очистить» такую таблицу, её уничтожают оператором УНИЧТОЖИТЬ в тексте следующего запроса:
УНИЧТОЖИТЬ ВремДанные
А чтобы сбросить все временные таблицы менеджера разом, закрывают сам менеджер:
Запрос.МенеджерВременныхТаблиц.Закрыть();
После Закрыть() все временные таблицы менеджера удаляются, и повторно обращаться к ним нельзя. Это и есть полная очистка временного хранилища запроса — в отличие от Очистить(), который работает с объектом в оперативной памяти.
Короткое правило: ТаблицаЗначений в памяти чистят методами Очистить() / пересозданием, а временные таблицы запроса — операторами УНИЧТОЖИТЬ или методом Закрыть() у менеджера.
Частые ошибки при очистке таблицы значений
Три промаха, на которых спотыкаются чаще всего:
- Удаление в прямом цикле. Перебор
Для КаждогосТЗ.Удалить()внутри пропускает строки или падает с ошибкой. Решение — обратный цикл по индексу или предварительныйНайтиСтроки(). - Путаница Очистить() и Колонки.Очистить(). Первый убирает строки, второй — колонки. Если после очистки «пропала структура», значит вызван не тот метод.
- Ожидание мгновенного освобождения памяти от Очистить(). Метод сбрасывает строки, но объект живёт. Если держите ссылку на большую таблицу, память не вернётся, пока ссылка жива. Нужна память — пересоздайте объект.
Для Сч = ТЗ.Количество() - 1 По 0 Цикл либо соберите строки через НайтиСтроки() и удалите по массиву.Очистить() вызван Колонки.Очистить(). Восстановите структуру колонок и используйте Очистить() только для строк.Очистить() не пересоздаёт объект. Присвойте переменной ТЗ = ТЗ.СкопироватьКолонки(), чтобы старые данные ушли под сборку мусора.Количество(). В обратном цикле берите границу из ТЗ.Количество() - 1 на старте.Очистка таблицы значений на управляемой форме
Отдельный случай — таблица значений как реквизит управляемой формы. Метод Очистить() здесь тоже работает, причём вызвать его можно и на клиенте, и на сервере:
&НаКлиенте
Процедура ОчиститьТаблицу(Команда)
ТаблицаНаФорме.Очистить();
КонецПроцедуры
Строки уйдут, колонки реквизита останутся — ровно как у таблицы в памяти. Это самый частый сценарий: кнопка «Очистить» на форме просто сбрасывает строки.
Сложнее, когда реквизит формы нужно не очистить, а заменить целиком — например, заполнить результатом запроса. Переприсваивать реквизит формы как обычную переменную в общем случае нельзя. Соберите таблицу в переменную на сервере и запишите её в реквизит методом ЗначениеВРеквизитФормы():
&НаСервере
Процедура ЗаполнитьНаСервере()
НоваяТаблица = ПолучитьТаблицуИзЗапроса();
ЗначениеВРеквизитФормы(НоваяТаблица, "ТаблицаНаФорме");
КонецПроцедуры
Обратное преобразование — РеквизитФормыВЗначение("ТаблицаНаФорме") — вернёт из реквизита обычный объект ТаблицаЗначений, с которым удобно работать в коде сервера (сортировать, сворачивать, чистить), а затем записать назад.
Оба метода, ЗначениеВРеквизитФормы() и РеквизитФормыВЗначение(), доступны только на сервере. С клиента их не вызвать — выносите логику в процедуру с директивой &НаСервере.
Если таблица — часть объекта (табличная часть документа или обработки), очистка та же: Объект.ТабличнаяЧасть.Очистить(). У табличных частей Очистить() тоже убирает строки и сохраняет колонки.
Отладка: как проверить, что таблица очистилась
Самый быстрый способ убедиться в результате — посмотреть Количество() в отладчике. Поставьте точку останова после очистки и в окне вычисления выражения выполните ТЗ.Количество() — должно быть 0.
Чтобы разом увидеть содержимое таблицы в отладке, в табло или окне вычисления раскройте переменную ТЗ: платформа покажет строки и колонки в виде таблицы. Если строк нет, а колонки видны — Очистить() отработал верно.
Для контроля в логике кода пригодится свойство количества:
Если ТЗ.Количество() = 0 Тогда
// таблица пуста, можно заполнять заново
КонецЕсли;
При массовой обработке данных выводите ТЗ.Количество() в журнал регистрации или через Сообщить() до и после очистки — так видно, что буфер действительно сбрасывается между итерациями, а не копит строки.
Типичная причина раздувания памяти в длинных обработках — таблицу-буфер объявили один раз, наполняют порциями, но забывают очищать между порциями. В итоге строки накапливаются, и к концу обработки в таблице не один пакет, а все сразу. Правило простое: таблица-буфер очищается в начале каждой итерации обработки, а не в конце:
Для Каждого Пакет Из ПакетыДанных Цикл
Буфер.Очистить();
ЗаполнитьБуфер(Буфер, Пакет);
ОбработатьБуфер(Буфер);
КонецЦикла;
Очистка в начале цикла надёжнее очистки в конце: даже если обработка пакета прервётся с ошибкой и до конца итерации дело не дойдёт, следующий проход всё равно начнёт с чистого буфера.
Частые вопросы
Чем отличается Очистить() от присвоения нового объекта?
Очистить() оставляет тот же объект и его колонки, просто убирает строки — память под объект сохраняется. Присвоение Новый ТаблицаЗначений или СкопироватьКолонки() создаёт другой объект, а старый уходит под сборку мусора. Для повторного наполнения берите Очистить(), для освобождения памяти — пересоздание.Как очистить только колонки, не трогая строки?
ТЗ.Колонки.Удалить("ИмяКолонки"). Удаление всех колонок через ТЗ.Колонки.Очистить() обнулит и данные.Почему при удалении строк в цикле часть остаётся?
НайтиСтроки() и удаляйте по полученному массиву.Как удалить строки по условию, а не все подряд?
Для Сч = ТЗ.Количество() - 1 По 0 Цикл с проверкой условия и ТЗ.Удалить(Сч), либо ТЗ.НайтиСтроки(Новый Структура("Колонка", Значение)) с перебором результата. Второй удобнее для простого равенства.