Удаление строк программно: таблица значений, табличная часть, макет и записи базы

· 9 мин

Содержание

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

Для табличного документа (печатной формы) нужен другой метод — УдалитьОбласть(). В запросе строки не удаляют вовсе: язык запросов только читает данные. Ниже код для каждого вида «таблицы» в 1С:Предприятии 8.3, который можно вставить в модуль из Конфигуратора и проверить.

Таблица значений: Удалить, Очистить и отбор копированием

Таблица значений живёт в памяти. Удаление из неё ничего не меняет в базе, поэтому здесь можно экспериментировать спокойно.

Метод Удалить() принимает один параметр: саму строку (СтрокаТаблицыЗначений) или её индекс. Индексы начинаются с нуля.

// Удалить первую строку
ТЗ.Удалить(0);

// Удалить конкретную строку
Стр = ТЗ.Найти("Иванов", "Фамилия");
Если Стр <> Неопределено Тогда
    ТЗ.Удалить(Стр);
КонецЕсли;

// Удалить все строки, колонки остаются
ТЗ.Очистить();

📌 Чем отличаются способы:

  • 🗑️ Удалить(Строка) — удобно, когда строка уже найдена через Найти() или пришла из цикла;
  • 🔢 Удалить(Индекс) — удобно в цикле по счётчику;
  • 🧹 Очистить() — убирает все строки разом, структура колонок сохраняется;
  • 📋 Скопировать(Отбор) — не удаляет, а создаёт новую таблицу только с нужными строками.

Последний вариант часто проще всего. Если нужно оставить только активные строки, удобнее скопировать подходящие, чем удалять лишние:

Отбор = Новый Структура("Активна", Истина);
ТЗ = ТЗ.Скопировать(Отбор);

Скопировать() с отбором по структуре сравнивает только на равенство. Условие «сумма больше 1000» так не задать: для него нужен цикл (см. раздел про обход коллекций).

Удаление строк, найденных через НайтиСтроки

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

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

Это самый короткий и надёжный способ удалить группу строк по точному совпадению значений.

Табличная часть документа и дерево значений

Табличная часть объекта (Объект.Товары) удаляет строки тем же методом Удалить(). После удаления НомерСтроки у оставшихся строк пересчитывается сам, вручную нумеровать ничего не нужно.

ДокОбъект = Ссылка.ПолучитьОбъект();
ДокОбъект.Товары.Удалить(0);
ДокОбъект.Записать();

У дерева значений строки удаляются из коллекции родителя: СтрокаДерева.Строки.Удалить(ПодчиненнаяСтрока). Верхний уровень — Дерево.Строки.Удалить(...). Вместе со строкой уходят все её подчинённые строки.

Как удалить строку табличной части на управляемой форме

На форме вы работаете не с самой табличной частью, а с данными формы (ДанныеФормыКоллекция). Элемент формы знает только идентификатор текущей строки, а не саму строку. Поэтому строку сначала получают по идентификатору:

&НаКлиенте
Процедура УдалитьТекущуюСтроку(Команда)
    ИдСтроки = Элементы.Товары.ТекущаяСтрока;
    Если ИдСтроки = Неопределено Тогда
        Возврат;
    КонецЕсли;
    Стр = Объект.Товары.НайтиПоИдентификатору(ИдСтроки);
    Объект.Товары.Удалить(Стр);
    Модифицированность = Истина;
КонецПроцедуры

Код работает на клиенте, серверный вызов не нужен.

💡 Программное изменение данных формы не всегда помечает форму изменённой. Строка Модифицированность = Истина гарантирует, что при закрытии 1С спросит про сохранение, и удаление не потеряется.

Удаление выделенных строк

Элементы.Товары.ВыделенныеСтроки — массив идентификаторов. Удаляя строки, вы меняете и выделение, поэтому сначала скопируйте идентификаторы в свой массив:

&НаКлиенте
Процедура УдалитьВыделенные(Команда)
    Идентификаторы = Новый Массив;
    Для Каждого Ид Из Элементы.Товары.ВыделенныеСтроки Цикл
        Идентификаторы.Добавить(Ид);
    КонецЦикла;

    Для Каждого Ид Из Идентификаторы Цикл
        Стр = Объект.Товары.НайтиПоИдентификатору(Ид);
        Если Стр <> Неопределено Тогда
            Объект.Товары.Удалить(Стр);
        КонецЕсли;
    КонецЦикла;
    Модифицированность = Истина;
КонецПроцедуры

Если пользователю нужно просто удалить строку руками, программировать ничего не нужно. В командной панели таблицы на форме есть стандартная команда удаления, работает и клавиша Delete. Код пишут, когда удаление идёт по условию или из обработки.

Табличный документ: удаляем строки печатной формы

Табличный документ (ТабличныйДокумент) — это не набор строк с колонками, а сетка ячеек. Здесь удаляют область целиком методом УдалитьОбласть() и указывают, куда сдвинуть оставшиеся ячейки.

// Удалить строки с 5-й по 7-ю, нижние строки поднимутся вверх
Область = ТабДок.Область("R5:R7");
ТабДок.УдалитьОбласть(Область, ТипСмещенияТабличногоДокумента.ПоВертикали);

📌 Что нужно знать:

  • 📐 "R5:R7" — область строк целиком, "C2" — колонка, "R5C2:R7C4" — прямоугольник;
  • ↕️ ПоВертикали сдвигает ячейки вверх, ПоГоризонтали — влево;
  • 🧮 после удаления нумерация строк смещается: если удаляете несколько областей, идите снизу вверх;
  • 🧹 ТабДок.Очистить() стирает весь документ, а не одну строку.

Чтобы удалить последнюю строку, номер берут из свойства ВысотаТаблицы:

Если ТабДок.ВысотаТаблицы > 0 Тогда
    НомерПоследней = Формат(ТабДок.ВысотаТаблицы, "ЧГ=0");
    Область = ТабДок.Область("R" + НомерПоследней);
    ТабДок.УдалитьОбласть(Область, ТипСмещенияТабличногоДокумента.ПоВертикали);
КонецЕсли;

Формат(..., "ЧГ=0") здесь обязателен. Без него число больше 999 превратится в строку с неразрывным пробелом («1 024»), и имя области будет неверным.

💡 В печатных формах удаление обычно не лучший путь. Проще не выводить ненужную строку: проверить условие перед ТабДок.Вывести(ОбластьСтроки). Удаление оставьте для готовых документов, которые пришли извне, например загруженных из файла Excel.

Временные таблицы и запросы: удаления нет, есть отбор и УНИЧТОЖИТЬ

В языке запросов 1С нет команды DELETE. Запрос только читает данные. Поэтому «удалить строку из результата запроса» означает одно из трёх:

ЗадачаКак решитьГде
Не получить лишние строкиУсловие в ГДЕ или ИМЕЮЩИЕТекст запроса
Убрать временную таблицу целикомУНИЧТОЖИТЬ ВТ_ИмяПакет запроса
Убрать строки после выполненияРезультат.Выгрузить() и Удалить() по таблице значенийКод модуля

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

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Остатки.Номенклатура КАК Номенклатура,
|    Остатки.КоличествоОстаток КАК Количество
|ПОМЕСТИТЬ ВТ_Остатки
|ИЗ
|    РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
|ГДЕ
|    Остатки.КоличествоОстаток > 0
|;
|
|ВЫБРАТЬ
|    ВТ_Остатки.Номенклатура,
|    ВТ_Остатки.Количество
|ИЗ
|    ВТ_Остатки КАК ВТ_Остатки
|;
|
|УНИЧТОЖИТЬ ВТ_Остатки";

Имя регистра в примере условное, подставьте своё.

Менеджер временных таблиц

Если временные таблицы живут в МенеджерВременныхТаблиц и переходят из запроса в запрос, удалить конкретную таблицу можно тем же УНИЧТОЖИТЬ в следующем запросе с этим менеджером. Все таблицы сразу освобождает МенеджерВТ.Закрыть().

Строку внутри временной таблицы удалить нельзя. Нужен другой состав — создайте новую временную таблицу запросом с нужным ГДЕ.

Удаление строк в цикле без пропусков

Главная ловушка — удалять строки внутри Для Каждого по той же коллекции. Ошибки не будет, но после удаления строки следующая сдвигается на её место, и цикл её проскакивает. Две подряд строки на удаление — одна останется.

Пример: в таблице 4 строки, удалить нужно 2-ю и 3-ю. Цикл удаляет 2-ю, 3-я встаёт на её место, цикл переходит к следующей позиции (бывшая 4-я). В итоге 3-я строка уцелела.

Способ 1. Обход с конца по индексу

При удалении меняются индексы только у строк после удалённой. Если идти от последней строки к первой, необработанные строки не сдвигаются.

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

Проверим на 4 строках: Всего = 4, индексы идут 3, 2, 1, 0. Удаление строки с индексом 2 не трогает строки 1 и 0, которые ещё впереди. Количество фиксируется до цикла в Всего и внутри не пересчитывается.

Способ 2. Сначала собрать, потом удалить

Этот вариант читается проще и подходит для любых коллекций, включая данные формы:

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

Для Каждого Стр Из КУдалению Цикл
    ТЗ.Удалить(Стр);
КонецЦикла;
СпособКогда братьНа что смотреть
НайтиСтроки() + удалениеОтбор по точному равенству полейНе подходит для «больше/меньше»
Обход с концаСложное условие, большие таблицыИндексы с нуля, Всего до цикла
Массив к удалениюНужна читаемость, данные формыЛишний проход по таблице
Скопировать(Отбор)Оставить строки по равенствуПолучается новая таблица
Условие в запросеДанные берутся из базыЛучший вариант по скорости

Удаление записей из таблиц базы данных

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

Перед массовым удалением из рабочей базы выгрузите копию: в Конфигураторе Администрирование → Выгрузить информационную базу (файл .dt) или через резервное копирование СУБД. Сначала проверьте код на копии.

Справочники и документы: пометка или сразу удаление

У объекта есть два пути:

  • 🏷️ Объект.УстановитьПометкуУдаления(Истина) — мягкий вариант. Объект остаётся в базе, а окончательно его удаляет обработка удаления помеченных объектов с проверкой ссылок. В БП 3.0 она находится в Администрирование → Обслуживание, группа «Корректировка данных».
  • ❌ Объект.Удалить() — непосредственное удаление без проверки ссылок. Документы, которые ссылались на объект, получат «битые» ссылки вида <Объект не найден>.
// Мягко: пометить
Об = Ссылка.ПолучитьОбъект();
Об.УстановитьПометкуУдаления(Истина);

// Жёстко: удалить сразу (только если уверены, что ссылок нет)
Об = Ссылка.ПолучитьОбъект();
Об.Удалить();

Для непосредственного удаления у роли пользователя должно быть право «Удаление» на этот объект (право «Интерактивное удаление» относится к удалению руками из формы) — иначе код упадёт с ошибкой нарушения прав доступа. Под полными правами такой проблемы нет.

Регистры: запись пустого набора

Записи регистра удаляют набором записей. Установите отбор и запишите набор пустым — записи, попавшие под отбор, будут удалены:

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

Без отбора Записать() пустого набора очистит весь регистр. Проверьте отбор дважды.

Для регистров, подчинённых регистратору (движения документа), отбор ставят по Регистратор. Правильнее же удалять движения через сам документ: отмена проведения убирает их штатно и не оставляет документ проведённым без движений.

💡 Для одной записи независимого регистра сведений удобен менеджер записи: заполнить измерения и период, вызвать Прочитать(), проверить Выбран() и вызвать Удалить().

Обработчики при удалении

При записи пустого набора и при Удалить() срабатывают подписки и обработчики ПередУдалением, ПередЗаписью. Иногда разработчики отключают их через ОбменДанными.Загрузка = Истина. Делайте так только в обработках переноса данных: проверки конфигурации при этом не выполняются, и в базе могут остаться несогласованные данные.

Ошибки при удалении строк и как их распознать

Диагностика: выберите симптом

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

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

Как удалить колонку из таблицы значений?
Через коллекцию колонок: ТЗ.Колонки.Удалить("ИмяКолонки"). Значения этой колонки во всех строках пропадут.
Можно ли удалить строку табличной части проведённого документа?
Да, но изменения попадут в движения только после перепроведения. Записывайте объект с режимом проведения, иначе табличная часть и движения разойдутся.
Как удалить пустые строки из таблицы значений?
Обходом с конца: проверяйте нужные поля через ЗначениеЗаполнено() и удаляйте строку по индексу, если все поля пустые.
Удаляет ли Очистить() таблицу значений целиком?
Нет. Удаляются только строки, колонки и их типы остаются. Таблицу можно сразу заполнять заново.
Почему после удаления строк табличной части номера строк идут без пропусков?
НомерСтроки у табличных частей пересчитывается автоматически. У таблицы значений такого поля нет: индекс просто сдвигается.