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

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

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

Способ 1: Объединение и подсчет количества (Метод группировки)

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

  1. Сначала мы объединяем данные из обоих источников в одну временную таблицу, используя оператор ОБЪЕДИНИТЬ ВСЕ.
  2. Затем мы группируем полученную таблицу по интересующему нас полю (например, Товар).
  3. В условии ИМЕЮЩИЕ мы отсекаем те записи, количество которых больше единицы.

Посмотрим на пример кода:


ВЫБРАТЬ
    Регистр1.Товар КАК Товар
ПОМЕСТИТЬ ВТ_ВсеТовары
ИЗ
    РегистрСведений.Регистр1 КАК Регистр1

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

ВЫБРАТЬ
    Регистр2.Товар
ИЗ
    РегистрСведений.Регистр2 КАК Регистр2
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ВсеТовары.Товар КАК Товар
ИЗ
    ВТ_ВсеТовары КАК ВТ_ВсеТовары
СГРУППИРОВАТЬ ПО
    ВТ_ВсеТовары.Товар
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(ВТ_ВсеТовары.Товар) = 1

Важный нюанс: Этот метод корректно работает только в том случае, если в исходных таблицах записи уже уникальны. Если внутри Регистр1 один и тот же товар встречается дважды, условие Количество = 1 его отсеет, даже если в Регистр2 его нет. Чтобы избежать этого, предварительно используйте ВЫБРАТЬ РАЗЛИЧНЫЕ в каждой части объединения.

Способ 2: Левое соединение и проверка на NULL

Если задача стоит более узко — найти только те товары, которые есть в первой таблице, но отсутствуют во второй, — идеально подходит ЛЕВОЕ СОЕДИНЕНИЕ. Проанализируем ситуацию: при левом соединении для записей из левой таблицы, которым не нашлось соответствия в правой, поля правой таблицы будут заполнены значением NULL. Это и есть наш маркер различия.

Рассмотрим пример запроса:


ВЫБРАТЬ
    Т1.Товар КАК Товар
ИЗ
    РегистрСведений.Регистр1 КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Регистр2 КАК Т2
    ПО Т1.Товар = Т2.Товар
ГДЕ
    Т2.Товар ЕСТЬ NULL

Если же нам нужно получить "разные" значения из обеих таблиц (симметрическую разность), мы можем объединить два таких запроса:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    Т1.Товар КАК Товар
ИЗ
    РегистрСведений.Регистр1 КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Регистр2 КАК Т2
    ПО Т1.Товар = Т2.Товар
ГДЕ
    Т2.Товар ЕСТЬ NULL

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

ВЫБРАТЬ РАЗЛИЧНЫЕ
    Т2.Товар
ИЗ
    РегистрСведений.Регистр2 КАК Т2
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Регистр1 КАК Т1
    ПО Т2.Товар = Т1.Товар
ГДЕ
    Т1.Товар ЕСТЬ NULL

Способ 3: Полное соединение

Оператор ПОЛНОЕ СОЕДИНЕНИЕ позволяет выполнить задачу в рамках одного пакета запроса без использования объединений (UNION). Мы соединяем две таблицы по ключевому полю и в секции ГДЕ указываем, что нас интересуют только те строки, где либо в первой, либо во второй таблице значение не определено (ЕСТЬ NULL).

Выясним, как это выглядит на практике:


ВЫБРАТЬ
    ЕСТЬNULL(Т1.Товар, Т2.Товар) КАК Товар
ИЗ
    РегистрСведений.Регистр1 КАК Т1
    ПОЛНОЕ СОЕДИНЕНИЕ РегистрСведений.Регистр2 КАК Т2
    ПО Т1.Товар = Т2.Товар
ГДЕ
    (Т1.Товар ЕСТЬ NULL ИЛИ Т2.Товар ЕСТЬ NULL)

Особенности производительности: Следует помнить, что ПОЛНОЕ СОЕДИНЕНИЕ может работать медленнее на СУБД PostgreSQL. Также использование условия ИЛИ в секции ГДЕ часто мешает оптимизатору запросов эффективно использовать индексы. Для высоконагруженных систем метод с ОБЪЕДИНИТЬ (Способ 2) часто оказывается предпочтительнее.

Способ 4: Метод "Весов" (Суммирование счетчиков)

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

  1. Из первого источника выбираем товары и добавляем поле-константу со значением 1.
  2. Из второго источника выбираем те же товары, но поле-константу задаем как -1.
  3. Объединяем эти наборы данных.
  4. Группируем по товару и суммируем наши константы.

Проанализируем результат: если сумма равна 0, значит товар есть в обеих таблицах в равном количестве. Если сумма отлична от нуля (1 или -1), значит товар уникален для одной из таблиц.


ВЫБРАТЬ
    Т.Товар КАК Товар,
    СУММА(Т.Счетчик) КАК ИтоговыйСчетчик
ИЗ
    (ВЫБРАТЬ Товар, 1 КАК Счетчик ИЗ РегистрСведений.Регистр1
     ОБЪЕДИНИТЬ ВСЕ
     ВЫБРАТЬ Товар, -1 КАК Счетчик ИЗ РегистрСведений.Регистр2) КАК Т
СГРУППИРОВАТЬ ПО
    Т.Товар
ИМЕЮЩИЕ
    СУММА(Т.Счетчик) <> 0

Рекомендации по оптимизации и типичные ошибки

При работе с большими объемами данных (десятки и сотни тысяч строк) выбор метода становится критичным. Рассмотрим основные правила:

  1. Индексация временных таблиц: Если вы помещаете данные из регистров во временные таблицы (ВТ) перед сравнением, обязательно добавляйте ИНДЕКСИРОВАТЬ ПО ключевому полю (например, Товар). Без индекса соединение таблиц превратится в крайне медленную операцию.
  2. Избегайте оператора "НЕ В": Запрос типа ГДЕ Товар НЕ В (ВЫБРАТЬ Товар ИЗ Регистр2) может работать очень медленно на больших массивах данных. Платформа 1С транслирует это в SQL-запрос, который не всегда оптимально обрабатывается сервером базы данных. Соединение (Join) почти всегда работает быстрее.
  3. Предварительная группировка: Если в ваших исходных таблицах (ТЗ или регистрах) возможны дубли по ключевым полям, всегда делайте предварительное сворачивание данных через СГРУППИРОВАТЬ ПО или РАЗЛИЧНЫЕ. В противном случае соединения могут привести к "размножению" строк (декартову произведению), что выдаст неверный результат.
  4. Сравнение по нескольким полям: Если уникальность записи определяется не одним полем Товар, а связкой Товар + Характеристика + Склад, не забудьте указать все эти поля в условии связи ПО и проверить каждое на NULL в секции ГДЕ.

Рассмотрим пример правильного сравнения по двум полям:


ВЫБРАТЬ
    Т1.Товар,
    Т1.Характеристика
ИЗ
    ВТ_Данные1 КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные2 КАК Т2
    ПО Т1.Товар = Т2.Товар
    И Т1.Характеристика = Т2.Характеристика
ГДЕ
    Т2.Товар ЕСТЬ NULL

Подводя итог, можно сказать, что универсального "лучшего" способа не существует. Для простых задач идеально подходит Способ 1 или Способ 2. Для сложных систем сверки данных, где важна скорость и работа с PostgreSQL, рекомендуем использовать Способ 4 или комбинацию левых соединений. Выбирайте метод исходя из объемов ваших данных и требований к производительности системы.

← На главную