Колонка в таблице значений: проверяем наличие до обращения

· 9 мин

Содержание

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

Метода с названием «НайтиКолонку» в платформе нет. Его часто ищут, но проверка живёт в коллекции колонок, а не в самой таблице. Ниже: как устроена эта коллекция, четыре способа проверки, разница между ними и особенности динамического списка, который таблицей значений не является.

Из чего состоит таблица значений и где хранятся колонки

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

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

Что умеет коллекция колонок:

  • 🔹 Найти(Имя) — вернуть колонку по имени или Неопределено;
  • 🔹 Количество() — сколько колонок в таблице;
  • 🔹 Получить(Индекс) и обращение через квадратные скобки — колонка по порядковому номеру, нумерация с нуля;
  • 🔹 Индекс(Колонка) — порядковый номер колонки в коллекции;
  • 🔹 Добавить(Имя, Тип, Заголовок, Ширина) — создать новую колонку;
  • 🔹 Удалить(Колонка) — убрать колонку вместе с данными.

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

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

Способ 1. Метод Найти: стандартная проверка

Это основной приём. Он короткий, читается с первого взгляда и не вызывает исключений.

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

Если нужна только проверка, а сама колонка дальше не нужна, пишите короче:

Если Таблица.Колонки.Найти("Сумма") = Неопределено Тогда
    Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
КонецЕсли;

Типичный сценарий — таблица пришла снаружи: из параметра функции, из файла, из результата чужой обработки. Вы не знаете наверняка, есть ли в ней нужная колонка, поэтому сначала проверяете, а при отсутствии создаёте.

Условие Если Таблица.Колонки.Найти(...) без сравнения с Неопределено работать не будет: объект колонки не приводится к булевому значению. Сравнение нужно всегда.

Как проверить сразу несколько колонок

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

Обязательные = Новый Массив;
Обязательные.Добавить("Номенклатура");
Обязательные.Добавить("Количество");
Обязательные.Добавить("Цена");

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

Если Отсутствующие.Количество() > 0 Тогда
    Сообщение = "В таблице не хватает колонок: " + СтрСоединить(Отсутствующие, ", ");
    ВызватьИсключение Сообщение;
КонецЕсли;

Пользователь получает один понятный текст со всем списком, а не по ошибке за раз. Для загрузки из Excel или CSV это особенно удобно.

Способ 2. Перебор колонок по индексу

Перебор нужен, когда проверка сложнее поиска по имени. Например, надо найти колонку по заголовку, по типу или по части имени.

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

Цикл Для Каждого Колонка Из Таблица.Колонки делает то же самое и короче. Счётчик по индексу берут, когда номер колонки пригодится дальше.

Таблица сравнения подходов:

СпособКогда использоватьРиски
Колонки.Найти(Имя)Известно точное имяНе найдёт колонку, если имя написано с опечаткой
Перебор Для КаждогоПоиск по заголовку, типу, маске имениБольше кода, медленнее при частых вызовах
Попытка … ИсключениеНужно «просто прочитать, если есть»Скрывает реальные ошибки, нельзя отличить причину сбоя
Результат запросаРаботаете с РезультатЗапроса, не с таблицейДругая коллекция колонок, но метод Найти тот же

Способ 3. Попытка и обработка исключения

Если обратиться к несуществующей колонке строки, платформа вызовет исключение «Поле объекта не обнаружено». На этом строят третий способ: пытаемся прочитать значение и перехватываем ошибку.

Попытка
    Значение = СтрокаТаблицы["Сумма"];
Исключение
    Значение = Неопределено;
КонецПопытки;

Работает, но это запасной вариант. Минусов три:

  1. Блок Попытка перехватывает любую ошибку, не только отсутствие колонки. Опечатка в имени переменной или недоступное значение молча превратятся в Неопределено.
  2. Исключение обходится дороже обычной проверки. В цикле на десятки тысяч строк разница заметна.
  3. Код хуже читается: непонятно, какой именно сбой ожидался.

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

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

Способ 4. Колонки в результате запроса и в структуре

Если данные пришли из запроса, таблицы значений у вас пока нет. Есть РезультатЗапроса, у него тоже есть коллекция Колонки с методом Найти. После выгрузки Результат.Выгрузить() получится обычная таблица значений с теми же колонками.

Результат = Запрос.Выполнить();
Если Результат.Колонки.Найти("Контрагент") <> Неопределено Тогда
    // поле «Контрагент» есть в выборке запроса
КонецЕсли;

Состав колонок запроса определяется текстом запроса, то есть секцией ВЫБРАТЬ. Для запроса, написанного вами, проверять нечего: вы знаете поля. Смысл есть, когда текст собирается динамически или запрос приходит из настройки пользователя.

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

Если Структура.Свойство("Сумма") Тогда
    // ключ есть
КонецЕсли;

Метод Свойство в нужный момент даже вернёт значение через второй параметр: Структура.Свойство("Сумма", Значение). Для соответствия, в свою очередь, подходит Получить(Ключ).

Где колонку проверять, чтобы не тормозило

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

Плохо:

Для Каждого Строка Из Таблица Цикл
    Если Таблица.Колонки.Найти("Скидка") <> Неопределено Тогда
        Итого = Итого + Строка.Скидка;
    КонецЕсли;
КонецЦикла;

Хорошо:

ЕстьСкидка = (Таблица.Колонки.Найти("Скидка") <> Неопределено);
Для Каждого Строка Из Таблица Цикл
    Если ЕстьСкидка Тогда
        Итого = Итого + Строка.Скидка;
    КонецЕсли;
КонецЦикла;

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

Что выбрать на практике:

  • ✅ Для обычного случая берите Колонки.Найти.
  • ✅ Для обязательного набора колонок — массив имён и один проход до обработки.
  • ✅ Для поиска по свойствам колонки — перебор.
  • ❌ Попытка вместо проверки оставляйте для неизвестных источников.

Точных замеров приводить не буду: скорость зависит от платформы и размера таблицы. Если сомневаетесь, замерьте на своих данных замером производительности (он есть в Конфигураторе и в режиме отладки).

Динамический список: почему там не работает Колонки.Найти

Динамический список на форме выглядит как таблица, но таблицей значений не является. Данные берутся из базы запросом, а у самого реквизита ДинамическийСписок нет коллекции Колонки, как у таблицы значений. Поэтому код вида Список.Колонки.Найти(...) вызовет ошибку.

Различайте две вещи:

  1. Поля источника. Это то, что выбирает запрос списка (основная таблица или произвольный запрос). Их набор задан в свойствах динамического списка в конфигураторе.
  2. Колонки элемента формы. Это то, что показано пользователю. Каждая колонка — отдельный элемент формы, и её можно найти среди подчинённых элементов таблицы.

Чтобы проверить, есть ли колонка в таблице на форме, используют подчинённые элементы:

Колонка = Элементы.Список.ПодчиненныеЭлементы.Найти("СписокСумма");
Если Колонка <> Неопределено Тогда
    Колонка.Видимость = Истина;
КонецЕсли;

Имя элемента формируется по схеме «имя таблицы формы + имя поля», так что для таблицы Список и поля Сумма получится СписокСумма. Точное имя посмотрите в конфигураторе: оно может быть переименовано.

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

Хотите работать с данными динамического списка как с таблицей значений? Выполните запрос с теми же условиями и выгрузите результат. Тогда Колонки.Найти заработает на выгрузке.

Типичные ошибки при работе с колонками

Коротко о том, на чём спотыкаются чаще всего.

Сравнение с пустой строкой вместо Неопределено. Найти возвращает не пустое значение и не Ложь, а именно Неопределено. Условие Если Колонка = "" никогда не сработает.

Поиск по заголовку вместо имени. В Найти передают имя, а не заголовок. Заголовок «Сумма к оплате» и имя СуммаКОплате — разные вещи. Если по заголовку нужно искать, берите перебор.

Лишние пробелы и опечатки в имени. Строка "Сумма " с пробелом на конце — другое имя. Метод ошибки не выдаст, просто вернёт Неопределено. Для коротких имён удобно завести константы или набор имён в одном месте.

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

Путаница с колонками формы. Таблица значений на форме и колонки элемента формы — разные вещи. Если реквизит формы имеет тип ТаблицаЗначений, его колонки видны в Объект.ИмяТаблицы.Колонки на сервере; а колонки отображения принадлежат элементу формы. Ошибка «Поле объекта не обнаружено» чаще всего означает, что вы обращаетесь не к тому объекту.

Как закрепить результат: небольшая функция

Если проверка нужна в нескольких местах, оформите её функцией в общем модуле или в модуле обработки:

Функция ЕстьКолонка(Таблица, ИмяКолонки) Экспорт
    Возврат Таблица.Колонки.Найти(ИмяКолонки) <> Неопределено;
КонецФункции

Для набора колонок удобнее функция, которая возвращает массив недостающих имён:

Функция НедостающиеКолонки(Таблица, ИменаКолонок) Экспорт
    Результат = Новый Массив;
    Для Каждого Имя Из ИменаКолонок Цикл
        Если Таблица.Колонки.Найти(Имя) = Неопределено Тогда
            Результат.Добавить(Имя);
        КонецЕсли;
    КонецЦикла;
    Возврат Результат;
КонецФункции

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

Мини-кейс: загрузка прайса с необязательной колонкой

Задача: обработка загружает прайс-лист из файла. Колонки «Артикул» и «Цена» обязательны, колонка «Скидка» бывает не у всех поставщиков.

Решение в три шага:

  1. Сразу после чтения файла проверяете обязательные колонки функцией НедостающиеКолонки. Если список не пустой, останавливаете загрузку и показываете его пользователю.
  2. Для необязательной колонки делаете один раз ЕстьСкидка = ЕстьКолонка(Таблица, "Скидка").
  3. В цикле по строкам читаете скидку только при ЕстьСкидка, иначе подставляете ноль.

Так обработка не падает на «кривом» файле и не молчит о проблеме: поставщик видит, чего именно не хватило.

Перед обращением к колонке
Выполнено: 0 / 5

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

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