В процессе разработки часто возникает задача дополнить существующую ТаблицаЗначений данными из регистров. Одной из самых типичных ситуаций является получение штрихкодов для списка номенклатуры. Рассмотрим, как перейти от медленных и ресурсоемких алгоритмов к профессиональным методам обработки данных в 1С.
Разберем ситуацию: у нас есть ТаблицаСерий (объект ТаблицаЗначений), в которой уже присутствуют колонки Номенклатура и ХарактеристикаНоменклатуры. Нам необходимо добавить колонку Штрихкод и заполнить ее данными из регистра сведений Штрихкоды.
Наивный подход заключается в том, чтобы в цикле по каждой строке таблицы искать данные в регистре или предварительно выгрузить весь регистр в память и использовать метод НайтиСтроки(). Однако, если в таблице сотни строк, а в регистре — десятки тысяч, производительность системы упадет до критического уровня. Проанализируем, как сделать это правильно.
Это наиболее эффективный и «правильный» с точки зрения архитектуры 1С способ. Мы передаем нашу таблицу в запрос, создаем из нее временную таблицу и соединяем ее с регистром сведений. Рассмотрим реализацию по шагам.
Шаг 1. Подготовка и типизация таблицы
Чтобы запрос смог обработать ТаблицаЗначений, ее колонки должны иметь строго определенные типы. Если таблица создана динамически без указания типов, запрос выдаст ошибку. Подготовим типизированную копию нашей таблицы:
// Создаем новую таблицу с типизированными колонками
ТС = Новый ТаблицаЗначений;
ТС.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТС.Колонки.Добавить("ХарактеристикаНоменклатуры", Новый ОписаниеТипов("СправочникСсылка.ХарактеристикиНоменклатуры"));
// Копируем данные из исходной ТаблицаСерий
Для Каждого СтрокаИзИсходной Из ТаблицаСерий Цикл
ЗаполнитьЗначенияСвойств(ТС.Добавить(), СтрокаИзИсходной);
КонецЦикла;
Использование ЗаполнитьЗначенияСвойств позволяет быстро перенести данные в новую структуру, не обращаясь к каждой колонке по имени вручную.
Шаг 2. Выполнение запроса с левым соединением
Теперь передадим подготовленную таблицу в запрос. Мы используем ЛЕВОЕ СОЕДИНЕНИЕ, чтобы сохранить все строки исходной таблицы, даже если для какой-то номенклатуры штрихкод в базе не найден.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_Исходная.Номенклатура КАК Номенклатура,
| ВТ_Исходная.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры
|ПОМЕСТИТЬ ПромТаблица
|ИЗ
| &ТЗ КАК ВТ_Исходная
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ПромТаблица.Номенклатура,
| ПромТаблица.ХарактеристикаНоменклатуры,
| ЕСТЬNULL(РегистрШтрихкоды.Штрихкод, """") КАК Штрихкод
|ИЗ
| ПромТаблица КАК ПромТаблица
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК РегистрШтрихкоды
| ПО ПромТаблица.Номенклатура = РегистрШтрихкоды.Владелец
| И ПромТаблица.ХарактеристикаНоменклатуры = РегистрШтрихкоды.ХарактеристикаНоменклатуры";
Запрос.УстановитьПараметр("ТЗ", ТС);
РезультатЗапроса = Запрос.Выполнить();
ТаблицаСоШтрихкодами = РезультатЗапроса.Выгрузить();
Важный нюанс: Обратите внимание на функцию ЕСТЬNULL(..., ""). Она гарантирует, что если штрихкод не найден, в колонку запишется пустая строка, а не значение NULL, которое может вызвать ошибки при дальнейшей программной обработке.
Если по каким-то причинам использование временных таблиц в запросе нежелательно (например, в очень специфических внешних обработках), можно применить метод сопоставления через Соответствие. Это работает быстрее, чем НайтиСтроки() в цикле.
Разберем алгоритм:
Соответствие, где ключом будет уникальный идентификатор (или комбинация полей), а значением — штрихкод.
// 1. Получаем список уникальных владельцев
СписокНом = ТаблицаСерий.ВыгрузитьКолонку("Номенклатура");
// 2. Запрос к регистру
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Штрихкоды.Владелец,
| Штрихкоды.ХарактеристикаНоменклатуры,
| Штрихкоды.Штрихкод
|ИЗ
| РегистрСведений.Штрихкоды КАК Штрихкоды
|ГДЕ
| Штрихкоды.Владелец В(&СписокНом)";
Запрос.УстановитьПараметр("СписокНом", СписокНом);
Выборка = Запрос.Выполнить().Выбрать();
// 3. Заполняем соответствие
КэшШтрихкодов = Новый Соответствие;
Пока Выборка.Следующий() Цикл
// Создаем ключ из Номенклатуры и Характеристики
Ключ = Строка(Выборка.Владелец.УникальныйИдентификатор()) + "_" + Строка(Выборка.ХарактеристикаНоменклатуры.УникальныйИдентификатор());
КэшШтрихкодов.Вставить(Ключ, Выборка.Штрихкод);
КонецЦикла;
// 4. Заполняем основную таблицу
Для Каждого Стр Из ТаблицаСерий Цикл
КлючПоиска = Строка(Стр.Номенклатура.УникальныйИдентификатор()) + "_" + Строка(Стр.ХарактеристикаНоменклатуры.УникальныйИдентификатор());
НайденныйШтрихкод = КэшШтрихкодов.Получить(КлючПоиска);
Стр.Штрихкод = ?(НайденныйШтрихкод = Неопределено, "", НайденныйШтрихкод);
КонецЦикла;
Проанализируем оба метода. Метод с запросом (№1) является эталонным. Он минимизирует количество передаваемых данных между сервером приложений и сервером СУБД. Вся тяжелая работа по сопоставлению строк выполняется на стороне SQL-сервера, который оптимизирован для таких задач.
Метод с соответствием (№2) полезен, когда нужно выполнить сложную логику проверки на стороне 1С, которую трудно описать в языке запросов. Однако он потребляет больше оперативной памяти на сервере приложений.
Рассмотрим важные советы по оптимизации:
ИНДЕКСИРОВАТЬ ПО для полей связи (Номенклатура, Характеристика). Это ускорит ЛЕВОЕ СОЕДИНЕНИЕ в разы на больших объемах.МАКСИМУМ(Штрихкоды.Штрихкод) или отбор по основной упаковке.ОписаниеТипов при создании программных таблиц значений. Это не только требование для запросов, но и хорошая практика, предотвращающая появление значений некорректных типов в данных.Выясним причину частых ошибок: многие начинающие разработчики пытаются использовать ТаблицаЗначений.НайтиСтроки() внутри цикла. Помните, что каждый вызов этого метода заставляет систему просматривать таблицу, что при вложенном цикле превращает сложность алгоритма в квадратичную. Использование временных таблиц в запросе переводит решение в разряд линейных и профессиональных.
Таким образом, мы изучили основные способы обогащения данных в 1С. Рассмотренные примеры кода можно адаптировать не только для штрихкодов, но и для получения цен, остатков или любых других данных из регистров сведений и накопления.