Не удается найти строку в таблице значений 1С 7.7: разбираем ошибки метода НайтиЗначение

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Причина 1: Строки фиксированной длины и концевые пробелы

Рассмотрим первую ошибку, которую совершил автор темы при объявлении колонок таблицы значений. В его исходном коде присутствовала следующая строка:

ТЗ.НоваяКолонка("ОС","Строка",200);

Давайте проанализируем, что здесь происходит. Третий параметр метода НоваяКолонка определяет длину строки. Когда мы явно указываем длину в 1С 7.7, платформа создает колонку фиксированной длины. Это означает, что любое записанное в эту ячейку значение будет автоматически дополнено концевыми пробелами справа до ровно 200 символов.

Представим, что мы записываем туда наименование основного средства: "Станок сверлильный". В памяти таблицы значений эта строка будет выглядеть как "Станок сверлильный" и еще 181 пробел справа. Когда мы пытаемся выполнить поиск значения с помощью функции НайтиЗначение:


// В Выбор.Наименование хранится "Станок сверлильный" (длина 19 символов)
Если ТЗ.НайтиЗначение(СокрЛП(Выбор.Наименование), С, "ОС") = 1 Тогда
    // Код обработки
КонецЕсли;

Метод НайтиЗначение осуществляет строгое посимвольное сравнение. Платформа сравнивает строку "Станок сверлильный" (длина 19) со строкой "Станок сверлильный ... [181 пробел]" (длина 200). Разумеется, эти строки не равны, и поиск возвращает 0.

Решение проблемы: Для строковых колонок в таблицах значений практически никогда не следует указывать фиксированную длину. Объявите колонку как строку неограниченной длины:

ТЗ.НоваяКолонка("ОС","Строка");

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

Причина 2: Ловушка переменной номера строки (параметр «С»)

Вторая распространенная ошибка связана с неправильным использованием переменной, в которую возвращается номер найденной строки. Синтаксис метода выглядит так:

НайтиЗначение(<Знач>, <Строка>, <Колонка>)

Второй параметр <Строка> передается по ссылке и имеет важнейшую техническую особенность:

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

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


Перем С; // Переменная объявлена, но не инициализирована (равна "")
Пока Выборка.ПолучитьЭлемент() = 1 Цикл
    // Первый проход: С равно "". Поиск идет по всей таблице.
    // Допустим, значение найдено в строке 5. Переменная С становится равной 5!
    Если ТЗ.НайтиЗначение(Выборка.Наименование, С, "ОС") = 1 Тогда
        // На втором шаге цикла переменная С уже равна 5!
        // Платформа будет искать только в 5-й строке ТЗ! 
        // Все остальные строки таблицы будут проигнорированы.
    КонецЕсли;
КонецЦикла;

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


С = 0; 
Если ТЗ.НайтиЗначение(Выбор.Наименование, С, "ОС") = 1 Тогда
    ТЗ.ПолучитьСтрокуПоНомеру(С);
    // Обработка данных
КонецЕсли;

Причина 3: Несоответствие типов данных при поиске по коду

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

ТЗ.НоваяКолонка("КодОС", "Число");

В конфигурациях 1С 7.7 справочники практически всегда имеют текстовый тип кода в метаданных (даже если визуально код состоит только из цифр, например "0000024"). Это сделано для корректного автонумератора с ведущими нулями.

Проанализируем цепочку событий:

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

Решение проблемы: Чтобы избежать конфликта типов, у нас есть два пути. Мы можем явно приводить искомое значение к числу:


С = 0;
Если ТЗ.НайтиЗначение(Число(Выбор.Код), С, "КодОС") = 1 Тогда
    // Код найден
КонецЕсли;

Либо (что гораздо более надежно и правильно) хранить код как строковое значение без принудительной типизации колонки ТЗ в число:

ТЗ.НоваяКолонка("КодОС", "Строка");

Лучшая практика: Хранение и поиск по объектным ссылкам

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

Давайте посмотрим на пример правильного заполнения таблицы значений и последующего поиска:


// Шаг 1. Создаем колонки. Колонку "ОС" типизируем под наш справочник 
// (или оставляем без жесткого типа, тогда она сможет хранить любые ссылки)
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("ОС", "Справочник.НеоборотныеАктивы");
ТЗ.НоваяКолонка("СчетДт");
ТЗ.НоваяКолонка("Сумма", "Число", 15, 2);

// Шаг 2. Заполняем таблицу значений напрямую из операции
Опер = СоздатьОбъект("Операция");
Опер.ВыбратьОперацииСПроводками(Дата1, Дата2, "*,423"); 
Пока Опер.ПолучитьПроводку() = 1 Цикл             
    ТЗ.НоваяСтрока(); 
    // Записываем сам объект ссылки (Субконто), а не строку наименования!
    ТЗ.ОС = Опер.Дебет.Субконто(2); 
    ТЗ.СчетДт = Опер.Дебет.Счет;    
    ТЗ.Сумма = Опер.Сумма;  
КонецЦикла;

// Шаг 3. Ищем строку таблицы значений по объекту ссылки
С = 0;
// Выбор - это реквизит формы с типом Справочник.НеоборотныеАктивы
Если ТЗ.НайтиЗначение(Выбор.ТекущийЭлемент(), С, "ОС") = 1 Тогда
    ТЗ.ПолучитьСтрокуПоНомеру(С);
    
    // Нам не нужно делать повторный НайтиПоКоду(), объект уже у нас в руках!
    СпрОС = СоздатьОбъект("Справочник.НеоборотныеАктивы");
    Если СпрОС.НайтиЭлемент(ТЗ.ОС) = 1 Тогда
        СпрОС.ПереоценкаПервСт = ТЗ.Сумма;
        СпрОС.Записать();
        Сообщить("Успешно обновлен элемент: " + ТЗ.ОС.Наименование);
    КонецЕсли;
Иначе
    Сообщить("Элемент не найден в таблице значений переоценки!");
КонецЕсли;

Преимущества объектного поиска перед строковым:

Применяя эти несложные правила типизации данных и очистки переменных поиска, вы навсегда избавитесь от загадочных ошибок при работе с методом НайтиЗначение в 1С:Предприятии 7.7.

← На главную