Соответствие 1С 8.3: Вставить, Получить, обход и примеры

Дата: 19.09.2026 04:42:56
Соответствие
Коллекции
ФиксированноеСоответствие
Встроенный язык
Оптимизация
Комментарий:
0
Платформа:
8.3
Конфигурация:
Любая
Формы:
Любые

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

Что такое соответствие 1С и где оно встречается

От структуры соответствие отличается тремя вещами. Ключ не обязан быть строкой. К элементу обращаются только через метод или квадратные скобки — точка не работает. Отсутствующий ключ при чтении не вызывает ошибку, а даёт Неопределено. Объект «Соответствие» есть в любой конфигурации на платформе 8.3 и доступен и на клиенте, и на сервере.

Где встречается в реальном коде:

  • кэш справочных данных в обработках загрузки: артикул или код из файла → ссылка в базе;
  • накопление итогов в памяти: контрагент → сумма, склад → количество;
  • отбор уникальных значений из массива;
  • чтение JSON: ПрочитатьJSON(Чтение, Истина) вернёт соответствие вместо структуры — это нужно, когда ключи JSON не годятся в имена переменных;
  • типовой код на БСП: ОбщегоНазначения.ЗначенияРеквизитовОбъектов() возвращает соответствие «ссылка → структура реквизитов».

Создание и заполнение: Вставить и квадратные скобки

Цены = Новый Соответствие;

Цены.Вставить("A-100", 12.5); // ключа не было - элемент добавится
Цены.Вставить("A-100", 14);   // ключ уже есть - значение заменится, ошибки нет
Цены["B-200"] = 30;           // то же, что Вставить: добавит или заменит

// Ключ - дата, значение - другая коллекция
ПродажиПоДням = Новый Соответствие;
ПродажиПоДням.Вставить('20260115', Новый Массив);

Цены.Удалить("B-200");        // удалить элемент по ключу
Всего = Цены.Количество();    // сколько элементов
Цены.Очистить();              // удалить всё

Конструктор без параметров создаёт пустое соответствие. Метод Вставить(Ключ, Значение) добавляет элемент, а если такой ключ уже есть — молча перезаписывает значение. Квадратные скобки слева от знака «равно» делают то же самое. Чтобы сохранить первое значение, проверьте ключ перед вставкой — как в примере с кэшем ниже.

Как получить значение: Получить и квадратные скобки

Цены = Новый Соответствие;
Цены.Вставить("A-100", 12.5);

Цена1 = Цены.Получить("A-100"); // 12.5
Цена2 = Цены["A-100"];          // 12.5 - то же самое
Цена3 = Цены.Получить("Z-999"); // Неопределено, ошибки нет
Цена4 = Цены["Z-999"];          // тоже Неопределено

// Цена5 = Цены.Артикул;        // ошибка: через точку к соответствию не обращаются

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

Главная ловушка — Неопределено означает сразу две вещи: «ключа нет» и «ключ есть, но значение пустое». Отдельного метода для проверки наличия ключа, как Свойство() у структуры, у соответствия нет. Самый простой выход — не класть Неопределено в значения: для «множеств» пишите Истина, для числовых итогов — 0, для ссылок — ссылку или пустую ссылку.

ДействиеСоответствиеФиксированноеСоответствие
Добавить или заменитьВставить(Ключ, Значение), С[Ключ] = Значениенельзя
ПрочитатьПолучить(Ключ), С[Ключ]Получить(Ключ), С[Ключ]
Удалить элементУдалить(Ключ)нельзя
ОчиститьОчистить()нельзя
Число элементовКоличество()Количество()
ОбходДля Каждого … Из … ЦиклДля Каждого … Из … Цикл

Какие значения годятся в ключ

Соответствие сравнивает ключи так же, как оператор «=». Отсюда правила:

  • Ссылки — лучший ключ. Две ссылки на один элемент справочника равны, даже если получены разными запросами.
  • Строки сравниваются с учётом регистра: "Болт" и "болт" — разные ключи. Пробелы по краям тоже учитываются. Ключи из внешних файлов приводите к одному виду: ВРег(СокрЛП(Значение)).
  • Даты хранят время до секунды: '20260115' и '20260115093000' — разные ключи. Для группировки по дням приводите дату через НачалоДня(), по месяцам — через НачалоМесяца().
  • Числа сравниваются по значению: 5 и 5.00 — один ключ. А число 5 и строка "5" — разные, у них разный тип.
  • Массив, структура, таблица значений в роли ключа сравниваются как объекты, а не по содержимому. Две структуры с одинаковыми полями — два разных ключа.

Поэтому в ключ кладут значения примитивных типов и ссылки — то, что не меняется после создания. Если тип заранее неизвестен, проверьте его до вставки — приёмы есть в статье тип значения 1С. Когда ключ составной, например товар плюс склад, вложите одно соответствие в другое:

// Остатки[Номенклатура][Склад] = Количество
Процедура ДобавитьОстаток(Остатки, Номенклатура, Склад, Количество)

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

	Было = ПоСкладам.Получить(Склад);
	ПоСкладам.Вставить(Склад, ?(Было = Неопределено, 0, Было) + Количество);

КонецПроцедуры

Внутреннее соответствие хранится по ссылке: после Остатки.Вставить(Номенклатура, ПоСкладам) все изменения ПоСкладам видны через Остатки, вставлять его повторно не нужно.

Обход соответствия: КлючИЗначение

Долги = Новый Соответствие;
Долги.Вставить("ООО Альфа", 15000);
Долги.Вставить("ООО Бета", 0);
Долги.Вставить("ИП Гамма", 4200);

Для Каждого КлючИЗначение Из Долги Цикл
	Сообщить(КлючИЗначение.Ключ + ": " + Строка(КлючИЗначение.Значение));
КонецЦикла;

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

Для Каждого Ключ Из КУдалению Цикл
	Долги.Удалить(Ключ);
КонецЦикла;

Каждый элемент при обходе — объект типа КлючИЗначение с двумя свойствами: Ключ и Значение. Имя переменной цикла любое, КлючИЗначение — просто привычное.

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

Удалять элементы того же соответствия внутри Для Каждого не надо — коллекция меняется во время обхода, результат непредсказуем. Соберите ключи в массив и удалите после цикла, как во второй половине примера.

Соответствие как кэш вместо запроса в цикле

Типичная картина в обработке загрузки: из Excel пришло несколько тысяч строк с артикулами, и для каждой строки код ищет номенклатуру:

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

НайтиПоРеквизиту() — это запрос к базе. Сколько строк в файле, столько обращений к СУБД. В файловой базе на локальном диске это терпимо, в клиент-серверной время растёт вместе с числом строк. Та же беда с чтением реквизита через точку в цикле: СтрокаФайла.Номенклатура.ЕдиницаИзмерения тоже идёт в базу, если данных нет в кэше платформы.

Решение — один запрос на весь список, результат в соответствие, дальше поиск в памяти:

// Серверный код: общий модуль с флажком «Сервер» или модуль объекта обработки.
// Платформа 8.3. Справочник Номенклатура с реквизитом Артикул есть
// в УТ 11, БП 3.0, КА 2 и ERP 2; в другой конфигурации замените имена.

// Приводит артикул к виду, в котором он лежит ключом в кэше.
// Число из Excel переводим в строку без разделителя групп: "12345", а не "12 345".
Функция КлючАртикула(Артикул) Экспорт

	Если ТипЗнч(Артикул) = Тип("Число") Тогда
		Возврат Формат(Артикул, "ЧГ=0");
	КонецЕсли;

	Возврат ВРег(СокрЛП(Артикул));

КонецФункции

// Возвращает соответствие: ключ артикула (Строка) -> СправочникСсылка.Номенклатура.
// Все артикулы ищутся одним запросом.
Функция НоменклатураПоАртикулам(Артикулы) Экспорт

	Кэш = Новый Соответствие;
	Если Артикулы.Количество() = 0 Тогда
		Возврат Кэш;
	КонецЕсли;

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Номенклатура.Артикул КАК Артикул,
	|	Номенклатура.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	Номенклатура.Артикул В (&Артикулы)
	|	И Номенклатура.Артикул <> """"
	|	И НЕ Номенклатура.ПометкаУдаления
	|
	|УПОРЯДОЧИТЬ ПО
	|	Номенклатура.Код";
	Запрос.УстановитьПараметр("Артикулы", Артикулы);

	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Ключ = КлючАртикула(Выборка.Артикул);
		// Дубли артикула в справочнике: оставляем первый элемент по коду
		Если Кэш.Получить(Ключ) = Неопределено Тогда
			Кэш.Вставить(Ключ, Выборка.Ссылка);
		КонецЕсли;
	КонецЦикла;

	Возврат Кэш;

КонецФункции

// Заполняет колонку Номенклатура в таблице загрузки.
// ДанныеФайла - ТаблицаЗначений с колонками Артикул (Строка или Число) и Номенклатура.
// Возвращает количество строк, для которых номенклатура не найдена.
Функция СопоставитьНоменклатуру(ДанныеФайла) Экспорт

	// В запрос уходит тот же вид артикула, по которому потом ищем в кэше.
	// Пустые артикулы не ищем: иначе они сопоставятся со случайным элементом или группой.
	Уникальные = Новый Соответствие;
	Для Каждого СтрокаФайла Из ДанныеФайла Цикл
		Ключ = КлючАртикула(СтрокаФайла.Артикул);
		Если Ключ <> "" Тогда
			Уникальные.Вставить(Ключ, Истина);
		КонецЕсли;
	КонецЦикла;

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

	Кэш = НоменклатураПоАртикулам(Артикулы);

	НеНайдено = 0;
	Для Каждого СтрокаФайла Из ДанныеФайла Цикл
		// Пустого ключа в кэше нет: Получить("") вернёт Неопределено
		Найдено = Кэш.Получить(КлючАртикула(СтрокаФайла.Артикул));
		Если Найдено = Неопределено Тогда
			НеНайдено = НеНайдено + 1;
		Иначе
			СтрокаФайла.Номенклатура = Найдено;
		КонецЕсли;
	КонецЦикла;

	Возврат НеНайдено;

КонецФункции

// Замер: запрос в цикле против кэша. Запускайте на копии базы и на одном файле.
Процедура СравнитьСпособы(ДанныеФайла) Экспорт

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

	Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
	СопоставитьНоменклатуру(ДанныеФайла);
	ВремяСКэшем = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;

	Сообщить("Строк: " + XMLСтрока(ДанныеФайла.Количество())
		+ "; запрос в цикле, мс: " + XMLСтрока(ВремяВЦикле)
		+ "; соответствие, мс: " + XMLСтрока(ВремяСКэшем));

КонецПроцедуры

Разбор ключевых строк

  • КлючАртикула() вызывается при подготовке параметра, при заполнении кэша и при поиске. Запрос сравнивает строки без учёта регистра и находит «a-100» по «A-100», а соответствие — с учётом. Без единой функции нормализации часть строк окажется «не найдена», хотя запрос их вернул.
  • Если Кэш.Получить(Ключ) = Неопределено — защита от дублей артикула. Благодаря УПОРЯДОЧИТЬ ПО Код выбор первого элемента предсказуем: при повторном запуске загрузка сопоставит строки так же.
  • Ключи нормализуются до запроса. В параметр уходит массив уникальных непустых ключей — ровно то, что потом ищется в соответствии, поэтому пробелы из Excel не мешают. Условие Артикул <> "" в запросе — страховка на случай, если функцию вызовут с «сырым» массивом. Для десятков тысяч артикулов передайте таблицу значений и соедините её со справочником через временную таблицу.
  • Контекст — только сервер. На клиенте нет ни Запрос, ни менеджера Справочники. Из формы вызывайте эти функции через процедуру с директивой &НаСервере или через серверный общий модуль.

Как замерить ускорение

Процедура СравнитьСпособы() прогоняет оба варианта по одной таблице и выводит время через ТекущаяУниверсальнаяДатаВМиллисекундах() (есть с версии 8.2.17). Запускайте её на копии рабочей базы с реальным файлом: на десятке строк разницы не видно. Первый прогон прогревает кэш СУБД — повторите сравнение два-три раза. Во втором варианте обращение к базе одно, остальное — поиск в памяти.

Тот же приём работает для реквизитов: вместо Ссылка.Реквизит в цикле соберите ссылки в массив, одним запросом получите нужные поля и сложите в соответствие «ссылка → структура». В конфигурациях на БСП это делает готовая функция ОбщегоНазначения.ЗначенияРеквизитовОбъектов(Ссылки, "Реквизит1, Реквизит2"). Про саму структуру и её методы — в статье структура 1С.

Ещё два приёма: итоги по ключу и уникальные значения

// Сумма оплат по контрагентам: данные уже в памяти, база не нужна
Итоги = Новый Соответствие;
Для Каждого СтрокаОплаты Из Оплаты Цикл
	Было = Итоги.Получить(СтрокаОплаты.Контрагент);
	Итоги.Вставить(СтрокаОплаты.Контрагент, ?(Было = Неопределено, 0, Было) + СтрокаОплаты.Сумма);
КонецЦикла;

// Уникальные значения массива: повторная вставка того же ключа ничего не добавляет
Уникальные = Новый Соответствие;
Для Каждого Код Из КодыИзФайла Цикл
	Уникальные.Вставить(Код, Истина);
КонецЦикла;

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

Для нового контрагента Получить() вернёт Неопределено, и ?(Было = Неопределено, 0, Было) превратит его в ноль. Если данные уже лежат в таблице значений и нужна только сумма, короче вызвать Свернуть("Контрагент", "Сумма"). Соответствие выгоднее, когда итоги собираются из разных источников или потом по ним часто ищут.

ФиксированноеСоответствие и передача между клиентом и сервером

Настройки = Новый Соответствие;
Настройки.Вставить("НДС", 20);

Фикс = Новый ФиксированноеСоответствие(Настройки); // снимок только для чтения
Ставка = Фикс.Получить("НДС");                     // читать можно

Копия = Новый Соответствие(Фикс);                  // изменяемая копия
Копия.Вставить("НДС", 22);

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

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

Типичные ошибки

  • Обращение через точку. Цены.Артикул вместо Цены["Артикул"] даёт ошибку «Поле объекта не обнаружено (Артикул)». Точка работает у структуры, у соответствия — только скобки и методы.
  • Регистр и пробелы в строковом ключе. Кэш заполнили из базы, ищут по данным из Excel — и часть строк «не найдена». Приводите ключ одной функцией и при заполнении, и при поиске.
  • Структура или массив в роли ключа. Кэш.Получить(Новый Структура("Товар, Склад", Товар, Склад)) всегда вернёт Неопределено: новая структура не равна той, что вставили раньше. Используйте вложенные соответствия.
  • Проверка Получить(Ключ) = Неопределено при пустых значениях. Ключ есть, значение Неопределено, а код считает, что ключа нет, и перезаписывает элемент. Не храните Неопределено в значениях.
  • Изменение фиксированного соответствия. Фикс.Вставить("НДС", 22) — ошибка «Метод объекта не обнаружен (Вставить)». Сделайте копию: Новый Соответствие(Фикс).
  • Пустой артикул в параметре запроса. Пустая ячейка файла даёт "", условие В (&Артикулы) находит все элементы без артикула и группы, и строка молча получает случайный элемент. Отсекайте пустые ключи до запроса.
  • Артикул из Excel пришёл числом. Неявное преобразование в строку ставит разделитель групп: 12345 становится «12 345», и строки не сопоставляются. Переводите явно: Формат(Артикул, "ЧГ=0").
  • Кэш собирается на клиенте. Функцию с запросом вызвали из клиентской процедуры формы — «Тип не определен (Запрос)». Перенесите её в серверный вызов.

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

Чем соответствие отличается от структуры?

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

Как проверить, есть ли ключ в соответствии?

Сравнить Получить(Ключ) с Неопределено. Это надёжно, если вы сами не кладёте Неопределено в значения; для «множеств» храните Истина.

Можно ли отсортировать соответствие?

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

Что быстрее: соответствие или поиск в таблице значений?

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

Когда проще заказать

Если обработка загрузки или отчёт тормозят на больших объёмах, частая причина — обращения к базе в цикле, и лечится это пакетным запросом и кэшем в соответствии. Такую доработку можно заказать в рамках услуги разработка отчётов и обработок 1С, а если медленно работает вся база — посмотрите раздел «1С тормозит». Или разместите задачу на площадке: оценка бесплатная, ответ — в течение рабочего дня.

По теме

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.


1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.