Работа со списками значений в 1С:Предприятие 8.3 — одна из самых частых задач, с которыми сталкиваются разработчики. Нужно ли найти различия между старыми и новыми данными, проверить дубликаты или синхронизировать справочники — без сравнения списков не обойтись. Но как сделать это эффективно, не теряя производительность и не усложняя код?

В этой статье мы разберём 7 рабочих методов сравнения списков в 1С, от простых встроенных функций до сложных алгоритмов с использованием запросов и временных таблиц. Вы узнаете, какой способ выбрать для больших массивов данных, как избежать типичных ошибок при сравнении, и получите готовые примеры кода для копирования. Особое внимание уделим нюансам работы с Неопределёнными значениями, регистром символов и производительностью при обработке тысяч строк.

Неважно, новичок вы или опытный программист — здесь найдётся решение для любой задачи. Давайте начнём с самого простого и постепенно перейдём к продвинутым техникам!

1. Метод Сравнить(): простое сравнение двух списков

Самый очевидный способ — использовать встроенный метод Сравнить(). Он возвращает Истина, если списки идентичны по составу и порядку элементов, и Ложь — если нет. Это удобно для быстрой проверки, но имеет ограничения.

Пример кода:

Список1 = Новый СписокЗначений;

Список1.Добавить("Яблоко");

Список1.Добавить("Банан");

Список2 = Новый СписокЗначений;

Список2.Добавить("Яблоко");

Список2.Добавить("Банан");

Результат = Список1.Сравнить(Список2); // Вернёт Истина

  • Плюсы: максимально простой синтаксис, не требует дополнительных действий.
  • Минусы:
    • Сравнивает в том числе порядок элементов — если порядок разный, вернёт Ложь даже при одинаковом составе.
    • Не показывает какие именно элементы отличаются.
    • Чувствителен к регистру символов (если сравниваете строки).
⚠️ Внимание: Метод Сравнить() не работает корректно, если в списках есть Неопределённые значения. В этом случае он всегда вернёт Ложь, даже если оба списка содержат Неопределённое в одних и тех же позициях.
💡

Если нужно сравнить списки без учёта порядка, предварительно отсортируйте их с помощью метода СортироватьПоЗначению() или СортироватьПоПредставлению()

2. Поиск совпадений и различий с помощью метода НайтиЗначение()

Когда требуется не просто проверить идентичность списков, а найти конкретные различия, на помощь приходит метод НайтиЗначение(). Он позволяет проверять наличие элемента в списке и получать его индекс.

Алгоритм работы:

  1. Перебираем элементы первого списка.
  2. Для каждого элемента проверяем его наличие во втором списке.
  3. Формируем списки уникальных и общих элементов.

Пример кода для поиска различий:

Процедура СравнитьСписки(Список1, Список2, УникальныеВ1, УникальныеВ2, Общие)

// Инициализируем результирующие списки

УникальныеВ1 = Новый СписокЗначений;

УникальныеВ2 = Новый СписокЗначений;

Общие = Новый СписокЗначений;

// Проверяем элементы первого списка

Для Каждого Элемент Из Список1 Цикл

Если Список2.НайтиЗначение(Элемент.Значение) = Неопределено Тогда

УникальныеВ1.Добавить(Элемент.Значение);

Иначе

Общие.Добавить(Элемент.Значение);

КонецЕсли;

КонецЦикла;

// Проверяем элементы второго списка

Для Каждого Элемент Из Список2 Цикл

Если Список1.НайтиЗначение(Элемент.Значение) = Неопределено Тогда

УникальныеВ2.Добавить(Элемент.Значение);

КонецЕсли;

КонецЦикла;

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

  • 🔍 Когда использовать: когда нужно получить детальный отчёт о различиях между списками.
  • Производительность: для списков до 1000 элементов работает быстро, для больших массивов лучше использовать Запрос.
  • 📌 Нюанс: метод НайтиЗначение() чувствителен к типу данных. Например, число 5 и строка "5" будут считаться разными значениями.
Как ускорить поиск в больших списках?

Для списков свыше 5000 элементов предварительно преобразуйте их в Массив и используйте метод Найти() — он работает быстрее, чем НайтиЗначение() для списков значений.

3. Использование запроса для сравнения списков

Для обработки больших объёмов данных (тысячи и десятки тысяч строк) оптимально использовать язык запросов 1С. Этот метод позволяет сравнивать списки прямо в базе данных, не загружая их в память.

Преимущества подхода:

  • 🚀 Высокая производительность даже для списков в 100 000+ элементов.
  • 🔧 Гибкость: можно сравнивать не только сами значения, но и связанные с ними данные (например, реквизиты справочников).
  • 📊 Агрегация результатов: легко получить статистику по совпадениям и различиям.

Пример запроса для сравнения двух списков:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Список1.Значение КАК Значение1,

| ЕСТЬNULL(Список2.Значение, """") КАК Значение2,

| ВЫБОР

| КОГДА Список2.Значение ЕСТЬ NULL

| ТОГДА ""Только в 1""

| КОГДА Список1.Значение = Список2.Значение

| ТОГДА ""Оба""

| ИНАЧЕ ""Только во 2""

| КОНЕЦ КАК Статус

|ИЗ

| (&Список1) КАК Список1

| ЛЕВОЕ СОЕДИНЕНИЕ (&Список2) КАК Список2

| ПО Список1.Значение = Список2.Значение";

Список1 = Новый ТаблицаЗначений;

Список1.Колонки.Добавить("Значение");

// Заполняем Список1 данными

Список2 = Новый ТаблицаЗначений;

Список2.Колонки.Добавить("Значение");

// Заполняем Список2 данными

Запрос.УстановитьПараметр("Список1", Список1);

Запрос.УстановитьПараметр("Список2", Список2);

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

⚠️ Внимание: При использовании запросов для сравнения списков значения параметров должны быть таблицами, а не списками значений. Преобразуйте их заранее с помощью метода Выгрузить().
Метод сравнения Макс. рекомендуемый размер списка Чувствительность к порядку Возвращает различия Производительность
Сравнить() До 100 элементов Да Нет ⚡ Мгновенно
НайтиЗначение() + цикл До 5000 элементов Нет Да 🚗 Быстро
Запрос 10 000+ элементов Нет Да 🚀 Очень быстро
Временные таблицы 100 000+ элементов Нет Да 🚀🚀 Максимальная
📊 Какой метод сравнения списков вы используете чаще?
Встроенный Сравнить()
Цикл с НайтиЗначение()
Запросы 1С
Своя функция на встроенном языке
Не сравниваю списки

4. Сравнение с учётом дополнительных полей (продвинутый подход)

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

В этом случае удобно использовать Структуру или ТаблицуЗначений для хранения дополнительной информации. Рассмотрим пример сравнения списков товаров с учётом кода и наименования:

Процедура СравнитьТовары(СписокТоваров1, СписокТоваров2)

// Создаём таблицы для результатов

ТолькоВпервом = Новый ТаблицаЗначений;

ТолькоВоВтором = Новый ТаблицаЗначений;

Изменённые = Новый ТаблицаЗначений;

Одинаковые = Новый ТаблицаЗначений;

// Добавляем колонки для результатов

Для Каждого Колонка Из СписокТоваров1.Колонки Цикл

ТолькоВпервом.Колонки.Добавить(Колонка.Имя);

Изменённые.Колонки.Добавить(Колонка.Имя);

Одинаковые.Колонки.Добавить(Колонка.Имя);

КонецЦикла;

// Сравниваем элементы

Для Каждого Товар1 Из СписокТоваров1 Цикл

НайденныйТовар2 = СписокТоваров2.НайтиСтроки(Новый Структура("Код", Товар1.Код)).Получить(0);

Если НайденныйТовар2 = Неопределено Тогда

ТолькоВпервом.Добавить();

ТолькоВпервом.ЗаполнитьЗначения(Товар1);

ИначеЕсли Не Товар1.Наименование = НайденныйТовар2.Наименование Тогда

Изменённые.Добавить();

Изменённые.ЗаполнитьЗначения(Товар1);

Изменённые.Наименование = "Было: " + НайденныйТовар2.Наименование + ", стало: " + Товар1.Наименование;

Иначе

Одинаковые.Добавить();

Одинаковые.ЗаполнитьЗначения(Товар1);

КонецЕсли;

КонецЦикла;

// Ищем элементы только во втором списке

Для Каждого Товар2 Из СписокТоваров2 Цикл

Если СписокТоваров1.НайтиСтроки(Новый Структура("Код", Товар2.Код)).Количество() = 0 Тогда

ТолькоВоВтором.Добавить();

ТолькоВоВтором.ЗаполнитьЗначения(Товар2);

КонецЕсли;

КонецЦикла;

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

Этот подход особенно полезен при:

  • 🔄 Синхронизации справочников между базами.
  • 📦 Контроле изменений в каталогах товаров.
  • 🔍 Поиске дублей с учётом нескольких критериев.

Проверьте, что все сравниваемые колонки имеют одинаковый тип данных|Убедитесь, что ключевые поля (например, "Код") уникальны|Преобразуйте пустые значения в NULL или "" для корректного сравнения|Создайте резервную копию данных перед массовыми изменениями-->

5. Оптимизация для больших списков: временные таблицы

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

Алгоритм работы:

  1. Создаём временные таблицы в базе данных.
  2. Загружаем в них данные из списков.
  3. Выполняем запрос с сравнением прямо на сервере.
  4. Получаем результат в виде таблицы различий.

Пример кода:

Процедура СравнитьБольшиеСписки(Список1, Список2)

// Создаём временные таблицы

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Список1.Значение КАК Значение,

| ""Список1"" КАК Источник

|ПОМЕСТИТЬ ВТ_Список1

|ИЗ

| &Список1 КАК Список1

|ОБЪЕДИНИТЬ ВСЕ

|ВЫБРАТЬ

| Список2.Значение КАК Значение,

| ""Список2"" КАК Источник

|ИЗ

| &Список2 КАК Список2";

Запрос.УстановитьПараметр("Список1", Список1.Выгрузить());

Запрос.УстановитьПараметр("Список2", Список2.Выгрузить());

Запрос.Выполнить();

// Анализируем различия

Запрос.Текст =

"ВЫБРАТЬ

| Значение,

| ВЫБОР

| КОГДА МИНИМУМ(Источник) = МАКСИМУМ(Источник)

| ТОГДА ""Оба""

| КОГДА МИНИМУМ(Источник) = ""Список1""

| ТОГДА ""Только в 1""

| ИНАЧЕ ""Только во 2""

| КОНЕЦ КАК Статус,

| КОЛИЧЕСТВО(*) КАК Количество

|ИЗ

| ВТ_Список1 КАК Данные

|СГРУППИРОВАТЬ ПО

| Значение";

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

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

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

Преимущества временных таблиц:

  • 💾 Минимальное потребление памяти на клиенте.
  • Максимальная скорость за счёт обработки на сервере СУБД.
  • 🔧 Гибкость: можно использовать любые конструкции SQL (JOIN, GROUP BY, HAVING).
💡

Для списков свыше 50 000 элементов временные таблицы — единственный эффективный способ сравнения. Они позволяют избежать переполнения памяти и "зависания" клиентского приложения.

6. Сравнение с учётом регистра и неопределённых значений

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

Как решить эти проблемы:

  • 🔤 Для игнорирования регистра:
    • Используйте функции НРег() или ВРег() при сравнении строк.
    • Пример: Если НРег(Строка1) = НРег(Строка2) Тогда...
  • Для обработки Неопределённого:
    • Явно проверяйте на Неопределено перед сравнением.
    • Используйте функцию ЗначениеЗаполнено().

Пример кода с учётом этих нюансов:

Функция БезопасноеСравнение(Значение1, Значение2, ИгнорироватьРегистр = Ложь)

// Обрабатываем Неопределённые значения

Если Значение1 = Неопределено ИЛИ Значение2 = Неопределено Тогда

Возврат (Значение1 = Неопределено) И (Значение2 = Неопределено);

КонецЕсли;

// Сравниваем с учётом регистра

Если ИгнорироватьРегистр И ТипЗнч(Значение1) = Тип("Строка") Тогда

Возврат НРег(Значение1) = НРег(Значение2);

Иначе

Возврат Значение1 = Значение2;

КонецЕсли;

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

Типичные ошибки и как их избежать:

  • 🚫 Ошибка: Сравнение Неопределённого с пустой строкой ("") как с одинаковыми значениями.
    Решение: Всегда проверяйте на Неопределено отдельно.
  • 🚫 Ошибка: Использование Сравнить() для списков с разным типом элементов (например, числа и строки).
    Решение: Приведите все элементы к одному типу перед сравнением.
  • 🚫 Ошибка: Игнорирование пробелов в строках.
    Решение: Используйте СокрЛП() для удаления лишних пробелов.

7. Альтернативные подходы: использование коллекций и внешних компонент

Если стандартные методы 1С не подходят (например, нужно сравнить списки по сложному алгоритму), можно рассмотреть альтернативные решения:

  • 📚 Коллекции (Соответствие, Множество):
    • Множество (Новый Множество) позволяет быстро проверять наличие элементов.
    • Соответствие (Новый Соответствие) удобно для хранения пар "ключ-значение".
  • 🖥️ Внешние компоненты:
    • Для сверхбольших массивов данных (миллионы записей) можно использовать OLAP-кубы или специализированные библиотеки.
    • Пример: компонента 1Script или интеграция с Python через COM.
  • 🔄 Алгоритмы слияния:
    • Для списков, отсортированных по ключу, эффективен алгоритм слияния (как в Объединить для массивов).

Пример использования Множества для быстрого поиска различий:

Функция СравнитьСпискиЧерезМножество(Список1, Список2)

Множество1 = Новый Множество;

Для Каждого Элемент Из Список1 Цикл

Множество1.Добавить(Элемент.Значение);

КонецЦикла;

Множество2 = Новый Множество;

Для Каждого Элемент Из Список2 Цикл

Множество2.Добавить(Элемент.Значение);

КонецЦикла;

// Находим уникальные элементы

ТолькоВ1 = Множество1.Вычесть(Множество2);

ТолькоВ2 = Множество2.Вычесть(Множество1);

Общие = Множество1.Пересечение(Множество2);

Возврат Новый Структура("ТолькоВ1, ТолькоВ2, Общие", ТолькоВ1, ТолькоВ2, Общие);

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

⚠️ Внимание: Внешние компоненты и сложные алгоритмы могут замедлить работу при неправильной реализации. Всегда тестируйте производительность на реальных данных перед внедрением в продакшн.

FAQ: Ответы на частые вопросы

Как сравнить списки значений по нескольким колонкам?

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

Критерий = Новый Структура("Код, Наименование", КодТовара, НаименованиеТовара);

НайденныеСтроки = Таблица.НайтиСтроки(Критерий);

Для сравнения двух таблиц создайте вложенный цикл и сравнивайте строки по ключевым полям.

Почему метод Сравнить() возвращает Ложь для одинаковых списков?

Наиболее вероятные причины:

  1. В списках разный порядок элементов (метод чувствителен к порядку).
  2. Элементы имеют разный тип данных (например, число 5 и строка "5").
  3. В списках есть Неопределённые значения.
  4. Элементы являются ссылками на объекты (даже если они ссылаются на один и тот же объект, сравнение может дать Ложь).

Решение: используйте поэлементное сравнение с приведением типов или метод НайтиЗначение().

Как ускорить сравнение списков из 100 000+ элементов?

Для таких объёмов данных:

  1. Используйте временные таблицы (описано в разделе 5).
  2. Разбейте списки на пакеты по 10 000 элементов и обрабатывайте их последовательно.
  3. Если сравнение идёт по ключевому полю (например, коду), отсортируйте списки перед сравнением — это ускорит поиск.
  4. Для текстовых данных используйте хеш-функции (например, ХешироватьДанные()) для быстрого сравнения.

Избегайте вложенных циклов — их сложность O(n²) сделает обработку крайне медленной.

Можно ли сравнить список значений с данными из справочника?

Да, для этого:

  1. Выгрузите данные справочника в ТаблицуЗначений или СписокЗначений.
  2. Используйте один из методов, описанных выше (например, НайтиЗначение() или запрос).

Пример:

СписокКодов = Новый СписокЗначений;

СписокКодов.Добавить("000001");

СписокКодов.Добавить("000002");

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Ссылка,

| Номенклатура.Код КАК Код

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| Номенклатура.Код В (&СписокКодов)";

Запрос.УстановитьПараметр("СписокКодов", СписокКодов);

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

Как сохранить результаты сравнения в файл?

Используйте ЗаписьТекста или ЗаписьJSON для сохранения результатов:

// Сохранение в CSV

Результат = Новый ТаблицаЗначений;

Результат.Колонки.Добавить("Значение");

Результат.Колонки.Добавить("Статус");

// ... заполняем результат

Запись = Новый ЗаписьТекста;

Запись.Открыть("C:\Temp\СравнениеСписков.csv", КодировкаТекста.UTF8);

Запись.ЗаписатьСтроку("Значение;Статус");

Для Каждого Строка Из Результат Цикл

Запись.ЗаписатьСтроку(Строка.Значение + ";" + Строка.Статус);

КонецЦикла;

Запись.Закрыть();

Для сложных структур удобнее использовать ЗаписьJSON.