Как правильно сопоставить данные из запроса с существующей таблицей значений в 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

В практике разработчика 1С часто возникает задача: есть таблица значений (ТЗ), заполненная данными из внешнего источника (Excel, XML, JSON), и в ней необходимо заполнить или обновить колонку (например, «Количество» или «Остаток»), получив актуальную информацию из базы данных через запрос. Рассмотрим, почему стандартный подход с вложенными циклами является неэффективным, и разберем наиболее правильные и быстрые способы решения этой задачи.

Проблема использования вложенных циклов

Рассмотрим ситуацию, с которой столкнулся автор темы. Имеется таблица ДанныеФайла и результат запроса РезультатЗапроса. Попытка сопоставить их через Для каждого внутри другого Для каждого — это классическая ошибка производительности. Если в таблице 1000 строк и в результате запроса 1000 строк, системе придется выполнить 1 000 000 операций сравнения. Это приводит к значительному замедлению работы интерфейса.

Кроме того, в коде автора была логическая ошибка, из-за которой цикл мог «пропускать» значения или работать некорректно. Проанализируем причины:

  1. Несоответствие типов: Артикул в таблице значений может быть строкой, а в запросе — ссылкой или строкой с лишними пробелами.
  2. Отсутствие очистки пробелов: При загрузке из внешних файлов часто попадаются невидимые символы.
  3. Логика итератора: Ошибки при ручном управлении циклом или неверное использование условия Если.

Способ 1. Соединение во временной таблице (Рекомендуемый)

Самый эффективный способ с точки зрения платформы 1С — это передать вашу таблицу значений прямо в тело запроса и выполнить соединение на стороне SQL-сервера. Это избавляет от необходимости писать циклы вообще. Рассмотрим этот метод по шагам.

Важное условие: для передачи ТЗ в запрос ее колонки должны быть жестко типизированы. Если вы создавали таблицу программно, убедитесь, что указали ОписаниеТипов.


// Подготовим таблицу к передаче в запрос
ДанныеФайла.Колонки.КаталожныйНомер.ТипЗнч = Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50));

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

// Помещаем ТЗ во временную таблицу
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_ДанныеФайла ИЗ &ТЗ КАК ТЗ";
Запрос.УстановитьПараметр("ТЗ", ДанныеФайла);
Запрос.Выполнить();

// Основной запрос с левым соединением
Запрос.Текст = 
"ВЫБРАТЬ
|	ВТ_ДанныеФайла.КаталожныйНомер КАК КаталожныйНомер,
|	ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) КАК Количество
|ИЗ
|	ВТ_ДанныеФайла КАК ВТ_ДанныеФайла
|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
|				,
|				Номенклатура.Артикул В
|					(ВЫБРАТЬ
|						ВТ.КаталожныйНомер
|					ИЗ
|						ВТ_ДанныеФайла КАК ВТ)) КАК ТоварыНаСкладахОстатки
|		ПО ВТ_ДанныеФайла.КаталожныйНомер = ТоварыНаСкладахОстатки.Номенклатура.Артикул";

ДанныеФайла = Запрос.Выполнить().Выгрузить();

Преимущества: Вся работа выполняется одной операцией. Нет риска ошибиться в логике перебора строк. Высокая скорость на больших объемах данных.

Способ 2. Использование индексации и метода НайтиСтроки()

Если вы не хотите использовать временные таблицы (например, данных очень мало и вызов tempdb избыточен), воспользуйтесь встроенными механизмами поиска 1С, но обязательно с предварительной индексацией. Посмотрим на пример оптимизированного кода.

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


// 1. Получаем данные из регистра по списку артикулов
СписокАртикулов = ДанныеФайла.ВыгрузитьКолонку("КаталожныйНомер");

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

Запрос.УстановитьПараметр("СписокАртикулов", СписокАртикулов);
РезультатЗапроса = Запрос.Выполнить().Выбрать();

// 2. Индексируем колонку для мгновенного поиска
ДанныеФайла.Индексы.Добавить("КаталожныйНомер");

// 3. Обходим результат запроса и обновляем ТЗ
Пока РезультатЗапроса.Следующий() Цикл
    СтруктураПоиска = Новый Структура("КаталожныйНомер", РезультатЗапроса.Артикул);
    НайденныеСтроки = ДанныеФайла.НайтиСтроки(СтруктураПоиска);
    
    Для Каждого СтрокаТЗ Из НайденныеСтроки Цикл
        СтрокаТЗ.Количество = РезультатЗапроса.Количество;
    КонецЦикла;
КонецЦикла;

Разберем подробнее: мы использовали НайтиСтроки(), так как в исходной таблице может быть несколько строк с одним и тем же артикулом. Если артикул гарантированно уникален, можно использовать метод Найти().

Способ 3. Использование объекта «Соответствие» (Map)

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

Рассмотрим алгоритм действий:

  1. Выгружаем результат запроса в Соответствие, где Ключ — Артикул, а Значение — Количество.
  2. Одним циклом проходим по исходной ТЗ и берем данные из соответствия.

// Сначала соберем данные запроса в "карту"
КартаОстатков = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    КартаОстатков.Вставить(Выборка.Артикул, Выборка.Количество);
КонецЦикла;

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

Важные рекомендации по отладке

Если данные все равно не проставляются, выполните следующие проверки:

Проверка на пробелы: Часто артикулы " 123" и "123 " не совпадают. Используйте функцию СокрЛП() как в запросе (через ПОДСТРОКА или предварительную обработку справочника), так и при заполнении таблицы.

Регистр символов: Артикулы "ABC" и "abc" для платформы могут быть разными строками. Рекомендуется приводить их к одному регистру функцией ВРег().

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

Подводя итог, можно сказать: забудьте о вложенных циклах. Используйте Способ 1 для надежности и Способ 3 для максимальной скорости при работе внутри кода. Это сделает вашу программу профессиональной и отзывчивой для пользователя.

← На главную